MAKE A ONE(MAYBE TWO)-BUTTON(S) GAME USING PICO8 #9 – ADD CHECKPOINT
In this post, we will add a checkpoint that will use for reset positions when the player’s character dies from fall out of the screen or hit obstacles.
The tasks for adding checkpoints and reset player position to the checkpoint.
1. modifying block data
2. modifying draw_block() function
3. create new functions for a collision between the player’s character and the checkpoint.
4. modify player_reset() function to support the checkpoint.
5. call player_reset() when the player’s character goes outsidethe of screen.
A BLOCK AND A CHECKPOINT
A maximum checkpoint in a block is one. From this logic. we will add a variable to keep the position of the checkpoint in the same way as the obstacle's position. (0 - 1) function create_block(x,y,w,h,left_wall,right_wall, floor, check_point) return {x=x,y=y,w=w,h=h,obstacles={},left_wall=left_wall,right_wall=right_wall,floor=floor,check_point=check_point} end And create a new variable for saving the current checkpoint.
current_check_point = nil
Next is modifying draw_block() function to support showing checkpoint inside the block and the checkpoint’s color will different base on “current_check_point” value.
function draw_block(b,c)
if (b == nil) return
if b.floor == true then
line(b.x,b.y,b.x+b.w,b.y,c)
end
if b.left_wall == true then
line(b.x,b.y,b.x,b.y-b.h,c)
end
if b.right_wall == true then
line(b.x+b.w,b.y,b.x+b.w,b.y-b.h,c)
end
for obs in all(b.obstacles) do
print("✽", b.x + (b.w*obs.value), b.y - 6, 8)
end
if b.check_point != nil then
if current_check_point != b then
print("♥", b.x + (b.w*b.check_point), b.y-6, 11)
else
print("♥", b.x + (b.w*b.check_point), b.y-6, 7)
end
end
end
Don’t forget to add a checkpoint when creating block data.
function _init() add(block_list, create_block(16,64,96,8,true ,true , true, nil)) add(block_list, create_block(16,56,96,8,true ,false, true, nil)) add(block_list, create_block(16,48,96,8,false,false , true, 0.5)) add(block_list, create_block(16,40,96,8,true,true , false, nil)) player_reset() end
THE PLAYER'S CHARACTER AND A CHECKPOINT
This part will make the player’s character can touch the checkpoint. A new function (“player_hit_check_point()”) will return block data when the player’s character touches the checkpoint and the checkpoint needs to be different from the last touching checkpoint.
function player_hit_check_point(b)
if current_check_point != b and b.check_point != nil then
if is_inside_block(player.x, player.y, b) == false then
return nil
else
local d = distance(player.x, player.y, (b.x + b.w * b.check_point), b.y)
if d < 3 then
return b
end
end
end
return nil
end
And modify “_update60()” function to use the new function.
function _update60()
player_running()
player_jumping()
player_drop_down()
player_apply_gravity()
player_find_new_current_block()
local hit_result = player_hit_obstacle(current_block)
if hit_result == true then
player_reset()
end
local chk_point = player_hit_check_point(current_block)
if chk_point != nil then
current_check_point = chk_point
end
update_camera()
end
RESET THE PLAYER'S CHARACTER POSITION
For the last part, we will make the player’s character reset position to the last touching checkpoint when it go outside of the screen. First, we create a new function for checking that the given position stays inside the screen or not.
function is_inside_screen(x,y)
if x >= camera_data.x and x <= 127 + camera_data.x then
if y >= camera_data.y and y <= 127 + camera_data.y then
return true
end
end
return false
end
Second, We modifying “player_reset()” function to use value from “current_check_point” variable.
function player_reset()
player = {x=64,y=64,di=1,jump_counter=0,ground=true}
change_current_block(block_list[1])
if current_check_point != nil then
player.x = current_check_point.x + current_check_point.w * current_check_point.check_point
player.y = current_check_point.y
change_current_block(current_check_point)
end
end
Last, we modify “_update60()” function to support the new checking function.
function _update60()
player_running()
player_jumping()
player_drop_down()
player_apply_gravity()
player_find_new_current_block()
local hit_result = player_hit_obstacle(current_block)
if hit_result == true then
player_reset()
end
local chk_point = player_hit_check_point(current_block)
if chk_point != nil then
current_check_point = chk_point
end
if is_inside_screen(player.x, player.y) == false then
player_reset()
end
update_camera()
end
RESULT

SOURCE CODE
player = {x=64,y=64,di=1,jump_counter=0,ground=true}
ignore_block = nil
current_block = nil
block_list = {}
current_check_point = nil
function create_block(x,y,w,h,left_wall,right_wall, floor, check_point)
return {x=x,y=y,w=w,h=h,obstacles={},left_wall=left_wall,right_wall=right_wall,floor=floor,check_point=check_point}
end
function draw_block(b,c)
if (b == nil) return
if b.floor == true then
line(b.x,b.y,b.x+b.w,b.y,c)
end
if b.left_wall == true then
line(b.x,b.y,b.x,b.y-b.h,c)
end
if b.right_wall == true then
line(b.x+b.w,b.y,b.x+b.w,b.y-b.h,c)
end
for obs in all(b.obstacles) do
print("✽", b.x + (b.w*obs.value), b.y - 6, 8)
end
if b.check_point != nil then
if current_check_point != b then
print("♥", b.x + (b.w*b.check_point), b.y-6, 11)
else
print("♥", b.x + (b.w*b.check_point), b.y-6, 7)
end
end
end
function distance(x1,y1,x2,y2)
local dx = x1-x2
local dy = y1-y2
local distance = sqrt(dx*dx+dy*dy)
return distance
end
function change_current_block(b)
current_block = b
if current_block != ignore_block then
ignore_block = nil
end
local d = distance(camera_data.target_x + 64, camera_data.target_y + 64, current_block.x + current_block.w/2, current_block.y - current_block.h/2)
_debug = d
if d > 12 then
set_camera_target(current_block)
end
end
function add_obstacle(b,v)
if (b == nil) return
add(b.obstacles, {value=v})
end
function is_inside_block(x,y,b)
if (b == nil) return
if x >= b.x and x < b.x + b.w then
if y <= b.y and y > b.y - b.h then
return true
end
end
return false
end
function is_inside_screen(x,y)
if x >= camera_data.x and x <= 127 + camera_data.x then
if y >= camera_data.y and y <= 127 + camera_data.y then
return true
end
end
return false
end
function player_running()
local next_px = player.x + player.di
if is_inside_block(player.x, player.y, current_block) == true and
is_inside_block(next_px, player.y, current_block) == false then
if player.di == 1 and current_block.right_wall == false then
elseif player.di == -1 and current_block.left_wall == false then
else
player.di = player.di * -1
next_px = player.x + player.di
end
end
player.x = next_px
end
function player_jumping()
if btnp(4) then
if player.jump_counter == 0 and player.ground == true then
player.jump_counter = 20
player.ground = false
end
end
local next_py = player.y
if player.jump_counter > 0 then
player.jump_counter -= 1
if player.jump_counter > 10 then
next_py = player.y - 1
else
next_py = player.y
end
end
player.y = next_py
end
function player_drop_down()
if btnp(5) and player.ground == true and
current_block != nil and
ignore_block == nil then
ignore_block = current_block
end
end
function player_apply_gravity()
local next_py = player.y
if player.ground == false then
if player.jump_counter <= 0 then
next_py += 1
if is_inside_block(player.x, player.y, current_block) == true and
is_inside_block(player.x, next_py, current_block) == false and
current_block.floor == true
then
if current_block != ignore_block then
next_py = player.y
player.ground = true
end
end
end
else
if is_inside_block(player.x, player.y, current_block) == false or current_block == ignore_block then
player.ground = false
end
end
player.y = next_py
end
function player_find_new_current_block()
if player.ground == false and
is_inside_block(player.x, player.y, current_block) == false then
for b in all(block_list) do
if is_inside_block(player.x, player.y, b) == true then
change_current_block(b)
break
end
end
end
end
function player_hit_obstacle(b)
if b == nil then return false end
if is_inside_block(player.x, player.y, current_block) == false then return false end
for o in all(b.obstacles) do
local d = distance(player.x, player.y, (b.x + b.w * o.value), b.y)
if d < 3 then
return true
end
end
end
function player_hit_check_point(b)
if current_check_point != b and b.check_point != nil then
if is_inside_block(player.x, player.y, b) == false then
return nil
else
local d = distance(player.x, player.y, (b.x + b.w * b.check_point), b.y)
if d < 3 then
return b
end
end
end
return nil
end
function player_reset()
player = {x=64,y=64,di=1,jump_counter=0,ground=true}
change_current_block(block_list[1])
if current_check_point != nil then
player.x = current_check_point.x + current_check_point.w * current_check_point.check_point
player.y = current_check_point.y
change_current_block(current_check_point)
end
end
camera_data = {x=0,y=0,target_x=0,target_y=0}
function set_camera_target(b)
if b == nil then return end
local block_center_x = b.x + b.w/2
local block_center_y = b.y - b.h/2
camera_data.target_x = block_center_x - 64
camera_data.target_y = block_center_y - 64
end
function update_camera()
if camera_data.x > camera_data.target_x then
camera_data.x -= 1
elseif camera_data.x < camera_data.target_x then
camera_data.x += 1
end
if camera_data.y > camera_data.target_y then
camera_data.y -= 1
elseif camera_data.y < camera_data.target_y then
camera_data.y += 1
end
camera(camera_data.x, camera_data.y)
end
function _init()
add(block_list, create_block(16,64,96,8,true ,true , true, nil))
add(block_list, create_block(16,56,96,8,true ,false, true, nil))
add(block_list, create_block(16,48,96,8,false,false , true, 0.5))
add(block_list, create_block(16,40,96,8,true,true , false, nil))
player_reset()
end
function _update60()
player_running()
player_jumping()
player_drop_down()
player_apply_gravity()
player_find_new_current_block()
local hit_result = player_hit_obstacle(current_block)
if hit_result == true then
player_reset()
end
local chk_point = player_hit_check_point(current_block)
if chk_point != nil then
current_check_point = chk_point
end
if is_inside_screen(player.x, player.y) == false then
player_reset()
end
update_camera()
end
function _draw()
cls(0)
print("웃", player.x-3, player.y - 5, 7)
for b in all(block_list) do
draw_block(b, 7)
end
end










