Build a Platformer Game¶
This tutorial walks you through building a complete platformer with animated sprites, gravity, jumping, platform collision, and camera scrolling.
What you will build¶
A side-scrolling platformer where the player can:
Move left and right
Jump between platforms
Fall with gravity
Respawn when falling off the screen
The camera follows the player horizontally.
Step 1: Prepare your sprites¶
Open the Sprite Editor and draw these sprites:
Sprite index |
Content |
|---|---|
|
Player standing (idle) |
|
Player walking frame 1 |
|
Player walking frame 2 |
|
Player jumping |
The player character is 8 pixels wide and 16 pixels tall (1 tile wide, 2 tiles tall).
Draw the top half of the character in the sprite slot and the bottom half in the slot directly
below it. The engine handles multi-tile drawing with sprite(index, x, y, 1, 2).
Tip
You can use any sprite indexes you want. Just update the constants at the top of the script to match.
Step 2: Paint your level¶
Open the Map Editor and paint your level:
Ground row – a full row of solid tiles across the bottom
Floating platforms – smaller groups of tiles at different heights
Example layout (each cell = 8 pixels):
Row 17 (y=136): platform at columns 9-13
Row 15 (y=120): platform at columns 17-20
Row 13 (y=104): platform at columns 25-31
Row 17 (y=136): platform at columns 34-37
Row 21 (y=168): ground spanning columns 0-52
The map is purely visual. Collision data lives in a Lua table that mirrors these positions (see Step 3).
Step 3: Write the code¶
Switch to the Code Editor and enter the full script below.
Sprite constants¶
-- Change these to match your sprite sheet
SPRITE_IDLE = 0
SPRITE_WALK_1 = 1
SPRITE_WALK_2 = 2
SPRITE_JUMP = 3
-- Player dimensions: 1 tile wide, 2 tiles tall (8x16 px)
PLAYER_W = 8
PLAYER_H = 16
Platform table¶
This table mirrors the solid tiles you painted in the Map Editor. Each entry is a rectangle in pixel coordinates.
platforms = {
{ x = -8, y = 168, w = 424, h = 8 }, -- ground
{ x = 72, y = 136, w = 40, h = 8 }, -- platform 1
{ x = 136, y = 120, w = 32, h = 8 }, -- platform 2
{ x = 200, y = 104, w = 56, h = 8 }, -- platform 3
{ x = 272, y = 136, w = 32, h = 8 }, -- platform 4
}
Note
Since tiles are 8 x 8 pixels, a platform at tile column 9, row 17, spanning 5 tiles is:
{ x = 9*8, y = 17*8, w = 5*8, h = 8 } which equals { x = 72, y = 136, w = 40, h = 8 }.
Helper functions¶
function clamp(v, lo, hi)
if v < lo then return lo end
if v > hi then return hi end
return v
end
function overlaps(ax, ay, aw, ah, bx, by, bw, bh)
return ax < bx + bw
and ax + aw > bx
and ay < by + bh
and ay + ah > by
end
Initialization¶
player = {}
anim_timer = 0
function _init()
player = {
x = 24,
y = 40,
vx = 0,
vy = 0,
speed = 1.8,
gravity = 0.30,
jump_force = -5.0,
max_fall = 5.5,
on_ground = false,
facing = 1,
anim_frame = SPRITE_IDLE,
}
anim_timer = 0
end
Input handling¶
function handle_input()
player.vx = 0
if key_pressed("ArrowLeft") or key_pressed("a") then
player.vx = -player.speed
player.facing = -1
end
if key_pressed("ArrowRight") or key_pressed("d") then
player.vx = player.speed
player.facing = 1
end
local wants_jump = key_pressed("ArrowUp")
or key_pressed("w")
or key_pressed(" ")
if wants_jump and player.on_ground then
player.vy = player.jump_force
player.on_ground = false
end
end
Animation¶
function update_animation()
if not player.on_ground then
player.anim_frame = SPRITE_JUMP
return
end
if player.vx ~= 0 then
anim_timer = anim_timer + 1
if anim_timer >= 8 then
anim_timer = 0
if player.anim_frame == SPRITE_WALK_1 then
player.anim_frame = SPRITE_WALK_2
else
player.anim_frame = SPRITE_WALK_1
end
end
else
player.anim_frame = SPRITE_IDLE
anim_timer = 0
end
end
Movement and collision¶
function move_x()
player.x = player.x + player.vx
for i = 1, #platforms do
local p = platforms[i]
if overlaps(player.x, player.y, PLAYER_W, PLAYER_H,
p.x, p.y, p.w, p.h) then
if player.vx > 0 then
player.x = p.x - PLAYER_W
elseif player.vx < 0 then
player.x = p.x + p.w
end
player.vx = 0
end
end
end
function move_y()
player.vy = player.vy + player.gravity
if player.vy > player.max_fall then
player.vy = player.max_fall
end
local prev_y = player.y
player.y = player.y + player.vy
player.on_ground = false
for i = 1, #platforms do
local p = platforms[i]
if overlaps(player.x, player.y, PLAYER_W, PLAYER_H,
p.x, p.y, p.w, p.h) then
local was_above = prev_y + PLAYER_H <= p.y
if was_above and player.vy >= 0 then
player.y = p.y - PLAYER_H
player.vy = 0
player.on_ground = true
elseif prev_y >= p.y + p.h then
player.y = p.y + p.h
player.vy = 0
end
end
end
-- Fell off the bottom: respawn
if player.y > 260 then
player.x = 24
player.y = 40
player.vx = 0
player.vy = 0
end
end
Game loop¶
function _update()
handle_input()
move_x()
move_y()
update_animation()
end
function draw_player()
sprite(player.anim_frame, player.x, player.y, 1, 2)
end
function _draw()
camera(clamp(player.x - 160, 0, 9999), 0)
clear(12)
map(0, 0)
draw_player()
end
How it all fits together¶
Sprite Editor Map Editor Lua Script
---------------- ---------------- --------------------------
Draw tile art at Paint solid tiles platforms table mirrors
index 0 = idle at matching tiles as pixel rectangles
index 1 = walk 1 positions for physics. map(0,0)
index 2 = walk 2 renders the tile art.
index 3 = jump sprite() draws the
animated character.
Extending the example¶
Add coins – Paint coin tiles on the map; add a
coinstable in Lua; check overlap each frame and remove collected entries.Add enemies – Add an
enemiestable; update positions each frame; usesprite()to draw them.Bigger player – Draw a 2x2 sprite and call
sprite(index, x, y, 2, 2).Animate tiles – Use
set_colto tint selected colors each frame.Level restart – Track a
livesvariable; reset player on death.