How to make a PICO-8 game: Part 10

How to make a PICO-8 game: Part 10

It’s time to put the final touches to our game, namely animating the player sprite. Previously I’ve shown you how to do this for the enemies, and also how to create the enemies as objects, so we’re combining those for this task.

Part 10: Player Animation

As before, we need to draw some frames of animation to use. I’ve created some additional frames in sprite slots 2, 3 and 4 to carry on from 1.

Again, it’s pretty simple but is just for illustration.

Now, we need to create the player as an object. We could have a function to house this, make_player() like we have make_enemy(), but we only need to create one player so that’s overkill. Instead, we’ll create it in one go in the _init() function, replacing the x and y we had there before:

 -- create player object
 player = {}
 player.x = 64
 player.y = 64
 player.sprite = 1
 player.frame = 0
 player.framemax = 3
 player.ticks = 0
 player.ticksmax = 4
 player.draw = function(this)
  this.ticks += 1
  if this.ticks == this.ticksmax then
   if this.frame == this.framemax then
    this.frame = 0
   else
    this.frame += 1
   end
   this.ticks = 0
  end
  spr(this.sprite+this.frame, this.x, this.y)
 end

You’ll notice I’ve put a player.draw function in there, which works just like the enemy draw function from last time.

Now we need to replace all references to x and y in the code, with player.x and player.y. You’ll find some in the button press check code (e.g. if btn(0) and x > 0 then…), in the collision check routine (if checkcol(x,y,en.x,en.y)) and in the en.move function (if x < this.x then…).

 if btn(0) and player.x > 0 then
  player.x -= 1
 end
 if btn(1) and player.x < 120 then
  player.x += 1
 end
 if btn(2) and player.y > 0 then
  player.y -= 1
 end
 if btn(3) and player.y < 120 then
  player.y += 1
 end
  if checkcol(player.x,player.y,en.x,en.y) then
   stop()
  end
 en.move = function(this)
  if player.x < this.x then this.x -= this.d end
  if player.x > this.x then this.x += this.d end
  if player.y < this.y then this.y -= this.d end
  if player.y > this.y then this.y += this.d end
 end

Finally, we need to replace the code in _draw() that drew the player sprite. It was spr(1,x,y), but now we just call:

player:draw()

Save, deep breath, run!

And that’s it. We have a complete, working game with enemies, movement, collisions and animation. Sure, we don’t have any sound, a title screen, lives or a game over sequence, but the game logic is there and you can see it’s a place to build from.

Here’s the final code. Skip past it to play the actual game!

function _init()
 -- create player object
 player = {}
 player.x = 64
 player.y = 64
 player.sprite = 1
 player.frame = 0
 player.framemax = 3
 player.ticks = 0
 player.ticksmax = 4
 player.draw = function(this)
  this.ticks += 1
  if this.ticks == this.ticksmax then
   if this.frame == this.framemax then
    this.frame = 0
   else
    this.frame += 1
   end
   this.ticks = 0
  end
  spr(this.sprite+this.frame, this.x, this.y)
 end
 
 ticks = 0
 tickstrigger = 200
 dinc = 0.05
 -- score
 score = 0
 
 -- initialise enemies table
 enemies = {}
 
 -- make first enemy
 make_enemy(17,120,120,0.05,3,0,5)
end

function _update()
 -- check for collision
 foreach(enemies, function(en)
  if checkcol(player.x,player.y,en.x,en.y) then
   stop()
  end
 end)

 --[[ check button presses and screen edge
  btn(0) is left
  btn(1) is right
  btn(2) is up
  btn(3) is down
 ]]
 if btn(0) and player.x > 0 then
  player.x -= 1
 end
 if btn(1) and player.x < 120 then
  player.x += 1
 end
 if btn(2) and player.y > 0 then
  player.y -= 1
 end
 if btn(3) and player.y < 120 then
  player.y += 1
 end

 -- move enemies
 foreach(enemies, function(en) en:move() end)
 
 -- increment score
 score += 1
 
 -- if ticks are triggered, make enemy faster
 ticks += 1
 if ticks > tickstrigger then
  foreach(enemies, function(en) en.d += dinc end)
  ticks = 0
  make_enemy(17,rnd(128),rnd(128),0.05,3,0,4)
 end
 
end

function _draw()
 cls(2) -- clear the screen to mauve
 print(score,100,0,7) -- print score
 player:draw() -- draw player
 -- draw all enemies
 foreach(enemies, function(en) en:draw() end)
end

function checkcol(ax,ay,bx,by)
 if bx+8>ax and bx<ax+8 and by+8>ay and by<ay+8 then
  return true
 else
  return false
 end
end

function make_enemy(espr,ex,ey,ed,eframemax,eticks,eticksmax)
 en = {}
 en.sprite = espr
 en.x = ex
 en.y = ey
 en.d = ed
 en.frame = 0
 en.framemax = eframemax
 en.ticks = eticks
 en.ticksmax = eticksmax
 
 en.draw = function(this)
  this.ticks += 1 -- increment ticks
  if this.ticks == this.ticksmax then
   -- have we hit the last tick?
   -- if so, inc the frame
   if this.frame == this.framemax then
    -- have we hit the last frame?
    this.frame = 0 -- set back to first frame
   else
    this.frame += 1 -- inc the frame
   end
   this.ticks = 0
  end 
  spr(this.sprite+this.frame,this.x,this.y)
 end
 
 en.move = function(this)
  if player.x < this.x then this.x -= this.d end
  if player.x > this.x then this.x += this.d end
  if player.y < this.y then this.y -= this.d end
  if player.y > this.y then this.y += this.d end
 end
 
 add(enemies,en)
end

[includeme src=”https://lofi-gaming.org.uk/gamedev/firstgame/firstgame.html” frameborder=”0″ width=”400″ height=”400″]

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.