MAKE A ONE-BUTTON GAME USING PICO8 #5 – CAMERA

This week, we will work on moving the camera to follow the player’s character when it changes the current block. The code that need to add can divide into 2 parts.
1. Move the camera to the target position
2. Detect when changing the current block and calculate for the target position.

MOVING CAMERA

For moving camera, It requires new variables to keep the current position and target position.

camera_data = {x=0,y=0,target_x=0,target_y=0}

And a new function for calculating the target position from a given block data. Why we need this function? because we want the target position shows at the center of the screen. It means we need to add offset values to the target position.

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

Now we have variables and a function for calculating the target position for the camera. we will implement a function to update the camera position.

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
CHANGE THE CAMERA'S TARGET POSITION

The last part is about to decide when to start moving the camera. The condition for changing is every time when the player’s character changes the current block and distance between the camera’s target position and center of the current block exceed the threshold.

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

   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
PUT EVERYTHING TOGETHER

We will start modifying code follow these steps.
1. Replacing the places that we assign value to the current block variable directly into set value via the new function.
2. Add update_camerat() into _update60()
3. Add more blocks in _init()
4. Modify player_hit_obstacle() to use distance()

RESULT
SOURCE CODE
player = {x=64,y=64,di=1,jump_counter=0,ground=true}
current_block = nil
block_list = {}

function create_block(x,y,w,h)
   return {x=x,y=y,w=w,h=h,obstacles={}}
end

function draw_block(b,c)
   if (b == nil) return
   rect(b.x,b.y,b.x+b.w,b.y-b.h,c)
   
   for obs in all(b.obstacles) do
      print("✽", b.x + (b.w*obs.value), b.y - 6, 8)
   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

   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 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

      player.di = player.di * -1
      next_px = player.x + player.di
   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_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 then

            next_py = player.y
            player.ground = true
         end
      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_reset()
   player = {x=64,y=64,di=1,jump_counter=0,ground=true}
   change_current_block(block_list[1])
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))
   add(block_list, create_block(16,56,96,8))
   add(block_list, create_block(16,48,96,8))
   add(block_list, create_block(16,40,96,8))
   add(block_list, create_block(16,32,96,8))
   player_reset()

   add_obstacle(block_list[2], 0.8)
   add_obstacle(block_list[3], 0.2)
   add_obstacle(block_list[3], 0.5)
   add_obstacle(block_list[5], 0.7)

end

function _update60()
   player_running()
   player_jumping()
   player_apply_gravity()
   player_find_new_current_block()
   local hit_result = player_hit_obstacle(current_block)
   if hit_result == true 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

Related Posts