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

0

Player standing (idle)

1

Player walking frame 1

2

Player walking frame 2

3

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 coins table in Lua; check overlap each frame and remove collected entries.

  • Add enemies – Add an enemies table; update positions each frame; use sprite() to draw them.

  • Bigger player – Draw a 2x2 sprite and call sprite(index, x, y, 2, 2).

  • Animate tiles – Use set_col to tint selected colors each frame.

  • Level restart – Track a lives variable; reset player on death.