Base project for 2D games with LÖVE 11.5 and Ark Engine.
love .├── ark_config.lua ← Engine config (resolution, title, scenes)
├── main.lua ← LÖVE entry point
├── ark_engine/ ← Engine (avoid modifying unless necessary)
│ ├── core/ ← Scene, View, Entity, Terminal
│ ├── managers/ ← Display, Scene, Sprite, Text, UI
│ ├── components/ ← Panel, Button, Overlay, Fade
│ ├── helpers/ ← Console, Helpers
│ └── fonts/ ← Engine fonts
├── libs/ ← flux (tweens), lurker (hot-reload)
├── assets/ ← Spritesheets and game resources
└── src/
├── gamestate.lua ← Global game state
├── data/
│ ├── SpritesDef.lua ← Sprite definitions
│ └── commands.lua ← Debug terminal commands
├── scenes/ ← Game scenes (extend Scene)
└── views/ ← Views/modals (extend View)
| Key | Action |
|---|---|
Space |
Add score |
Esc |
Pause / Close terminal |
` / Tab / Enter |
Open debug terminal |
Q (in pause) |
Quit game |
Cmd+R |
Reload current scene |
Open with ` and type commands:
give <n>— Add points to scorehelp— List available commandsfps— Show current FPSexit/restart— Quit or restart
- Create
src/scenes/my_scene.lua:
local Scene = require("ark_engine.core.Scene")
local MyScene = Scene:new()
function MyScene:onEnter()
-- setup
end
function MyScene:onDraw()
-- render
end
function MyScene:onKeyPressed(key)
-- input
end
return MyScene- Register it in
ark_config.lua:
SCENES = {
MY_SCENE = require("src.scenes.my_scene"),
}- Navigate to it:
Ark.scene:play(SCENES.MY_SCENE)
- Add the image to
assets/ - Register in
src/data/SpritesDef.lua:
local SPRITESHEETS = {
"assets/my_spritesheet.png", -- sheetIndex = 1
}
local SPRITE_DEFINITIONS = {
my_sprite = { sheetIndex = 0, x = 0, y = 0, w = 16, h = 16 },
}- Draw it:
Ark.spritesheet:draw(Ark.sprites.my_sprite, x, y)
Default pivot is bottom-center
(w/2, h).
Views block input from the scene below. Ideal for pauses, menus, popups.
local View = require("ark_engine.core.View")
local MyView = setmetatable({}, { __index = View })
MyView.__index = MyView
function MyView:new(scene)
local instance = View.new(self, scene)
instance.active = false
instance.visible = false
return instance
end
function MyView:onEnter()
self.active = true
self.visible = true
end
function MyView:onExit()
self.active = false
self.visible = false
end
function MyView:onDraw()
love.graphics.setColor(0, 0, 0, 0.5)
love.graphics.rectangle("fill", 0, 0, Ark.display.width, Ark.display.height)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.print("My Modal", 100, 100)
end
function MyView:onKeyPressed(key)
if key == "escape" then self:close() end
end
return MyViewFrom a scene:
self:openView(myView) -- Opens and blocks input
self:closeView(myView) -- Closes manually
-- Or from the view: self:close()Base class for objects with sprite, position, scale and outline.
local Entity = require("ark_engine.core.Entity")
local npc = Entity:new({
id = "npc_01",
sprite = Ark.sprites.player,
x = 100,
y = 150,
scaleX = 1,
scaleY = 1,
})
-- In onDraw:
npc:draw() -- Normal
npc:draw({ outline = 1 }) -- Thin outline (4 dirs)
npc:draw({ outline = 2 }) -- Thick outline (8 dirs)Animatable properties with flux: x, y, alpha, scaleX, scaleY, rotation.
-- Simple text with black outline
Ark.text:draw("Hello!", {
x = 100, y = 50,
color = { 1, 1, 1 },
})
-- Centered text
Ark.text:draw("Game Over", {
x = Ark.display.center_x,
y = Ark.display.center_y,
align = TEXT_ALIGN.CENTER,
big = true, -- 32px
})
-- Small text
Ark.text:draw("v1.0", {
x = 4, y = 4,
small = true, -- 8px, uses 04B_03 font
bold = false, -- Thin outline
})
-- Text with extra white outline
Ark.text:drawWithOutline("SCORE", {
x = 50, y = 10,
color = { 255, 200, 0 },
})Available alignments: TEXT_ALIGN.TOP_LEFT, TOP_CENTER, TOP_RIGHT, LEFT, CENTER, RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT.
Flux updates automatically in the engine. Just create tweens:
-- Move an entity
flux.to(entity, 0.5, { x = 200, y = 100 })
-- With easing
flux.to(entity, 0.3, { alpha = 0 }):ease("quadout")
-- Chain tweens
flux.to(entity, 0.2, { y = entity.y - 10 })
:ease("quadout")
:oncomplete(function()
flux.to(entity, 0.2, { y = entity.y + 10 }):ease("quadin")
end)
-- Delay before executing
flux.to(entity, 0.5, { x = 300 }):delay(1.0)Easing constants available via EASE: EASE.QUAD_OUT, EASE.BACK_OUT, EASE.ELASTIC_OUT, etc.
Common easings: "linear", "quadin", "quadout", "quadinout", "cubicin", "cubicout", "backin", "backout", "elasticout".
Each scene has an automatic self.seq for temporal coroutines:
function MyScene:onEnter()
self.seq:exec(function(seq)
print("Starting!")
seq:wait(1.0) -- Wait 1 second
print("Moving...")
flux.to(entity, 0.5, { x = 200 })
seq:wait(0.5) -- Wait for the tween to finish
print("Done!")
end)
end
seq:wait(n)pauses the coroutine for N seconds. Ideal for cutscenes, tutorials, multi-phase animations.
local Panel = require("ark_engine.components.Panel")
local panel = Panel:new({
title = "REWARDS",
x = 40, y = 40,
w = 240, h = 160,
bgColor = { 0, 0, 0 },
borderColor = { 255, 255, 0 },
render = function(alpha, w, h)
-- Draw content relative to the panel
love.graphics.setColor(1, 1, 1, alpha)
love.graphics.print("Content here", 10, 30)
end,
})
panel:fadeIn(0.3) -- Fade in
panel:fadeOut(0.3) -- Fade out
panel:draw() -- In scene's onDrawlocal Overlay = require("ark_engine.components.Overlay")
-- Semi-transparent dark background, typical behind modalslocal Fade = require("ark_engine.components.Fade")
-- Full-screen transitions (fade in/out)In src/data/commands.lua:
commands.my_command = function(args)
local value = tonumber(args[1]) or 0
print("Received: " .. value)
end
commands_help.my_command = "my_command <value> - Description."Usage in terminal: my_command 42
Ark.display.width -- Canvas width (320)
Ark.display.height -- Canvas height (240)
Ark.display.center_x -- Center X (160)
Ark.display.center_y -- Center Y (120)Lurker automatically reloads .lua files on save. No need to restart the game to see code changes. Just save and watch ✨
