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