Platformer – complete code¶
The finished script from the Build a Platformer Game tutorial, ready to compare against your own build.
-- ============================================================
-- 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, 1 tile tall (8x8 px)
PLAYER_W = 8
PLAYER_H = 8
-- Tilemap settings
TILE_SIZE = 8
MAP_W = 128
MAP_H = 32
SPRITE_COUNT = 256
FLAG_SOLID = 0
FLAG_KILL = 1
FLAG_END = 2
-- ============================================================
-- Helpers: the map is the collision data
-- ============================================================
function clamp(v, lo, hi)
if v < lo then return lo end
if v > hi then return hi end
return v
end
function tile_has_flag(tx, ty, flag)
if tx < 0 or tx >= MAP_W or ty < 0 or ty >= MAP_H then
return false
end
local sprite_index = mget(tx, ty)
if type(sprite_index) ~= "number" then
return false
end
if sprite_index < 0 or sprite_index >= SPRITE_COUNT then
return false
end
return fget(sprite_index, flag)
end
function is_solid_tile(tx, ty)
return tile_has_flag(tx, ty, FLAG_SOLID)
end
function player_touching_flag(flag)
local left_tile = math.floor(player.x / TILE_SIZE)
local right_tile = math.floor((player.x + PLAYER_W - 1) / TILE_SIZE)
local top_tile = math.floor(player.y / TILE_SIZE)
local bottom_tile = math.floor((player.y + PLAYER_H - 1) / TILE_SIZE)
for ty = top_tile, bottom_tile do
for tx = left_tile, right_tile do
if tile_has_flag(tx, ty, flag) then
return true
end
end
end
return false
end
-- ============================================================
-- Initialization
-- ============================================================
player = {}
anim_timer = 0
game_finished = false
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
game_finished = false
end
-- ============================================================
-- Input
-- ============================================================
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, one axis at a time
-- ============================================================
function move_x()
player.x = player.x + player.vx
local top_tile = math.floor(player.y / TILE_SIZE)
local bottom_tile = math.floor((player.y + PLAYER_H - 1) / TILE_SIZE)
if player.vx > 0 then
local right_tile = math.floor((player.x + PLAYER_W - 1) / TILE_SIZE)
for ty = top_tile, bottom_tile do
if is_solid_tile(right_tile, ty) then
player.x = right_tile * TILE_SIZE - PLAYER_W
player.vx = 0
break
end
end
elseif player.vx < 0 then
local left_tile = math.floor(player.x / TILE_SIZE)
for ty = top_tile, bottom_tile do
if is_solid_tile(left_tile, ty) then
player.x = (left_tile + 1) * TILE_SIZE
player.vx = 0
break
end
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
player.y = player.y + player.vy
player.on_ground = false
local left_tile = math.floor(player.x / TILE_SIZE)
local right_tile = math.floor((player.x + PLAYER_W - 1) / TILE_SIZE)
if player.vy > 0 then
local bottom_tile = math.floor((player.y + PLAYER_H) / TILE_SIZE)
for tx = left_tile, right_tile do
if is_solid_tile(tx, bottom_tile) then
player.y = bottom_tile * TILE_SIZE - PLAYER_H
player.vy = 0
player.on_ground = true
break
end
end
elseif player.vy < 0 then
local top_tile = math.floor(player.y / TILE_SIZE)
for tx = left_tile, right_tile do
if is_solid_tile(tx, top_tile) then
player.y = (top_tile + 1) * TILE_SIZE
player.vy = 0
break
end
end
end
-- Fell off the bottom: respawn
if player.y > 260 then
respawn_player()
end
end
-- ============================================================
-- Special tiles
-- ============================================================
function respawn_player()
player.x = 24
player.y = 40
player.vx = 0
player.vy = 0
player.on_ground = false
player.anim_frame = SPRITE_IDLE
end
function win_game()
if game_finished then
return
end
game_finished = true
player.vx = 0
player.vy = 0
-- Announce the win in the output panel
print("You Won")
end
function check_special_tiles()
if player_touching_flag(FLAG_KILL) then
respawn_player()
return
end
if player_touching_flag(FLAG_END) then
win_game()
end
end
-- ============================================================
-- Game loop
-- ============================================================
function _update()
if game_finished then
return
end
handle_input()
move_x()
move_y()
check_special_tiles()
-- check_special_tiles may have just won the game this frame
if game_finished then
return
end
update_animation()
end
function draw_player()
sprite(player.anim_frame, player.x, player.y, 1, 1)
end
function _draw()
camera(clamp(player.x - 160, 0, MAP_W * TILE_SIZE - 320), 0)
clear(12)
map(0, 0)
draw_player()
end