How to make a PICO-8 game: Part 9

How to make a PICO-8 game: Part 9

Sprites are all well and good, but if they animate they look so much better. Let’s head towards the end of this project by polishing it a bit.

Part 9: Animation

First up, we need to have some frames of animation for the enemy sprites. To make it a bit more logical on the sprite screen, I’ve moved the first frame from “slot” 2 to “slot” 17, then edited it and created three more frames of animation:

It’s nothing fancy, but you can change that if you like.

Now, when we create the enemy, we need to pass an extra parameter to the make_enemy() function – the total number of animation frames for the sprite. Of course we could hard code this into the enemy object, but lets assume we might want to reuse this function at another time for a range of objects each with a varying number of animation frames.

Currently, the function starts with this:

function make_enemy(espr,ex,ey,ed,eticks,eticksmax)

We’ll add “eframemax” to this:

function make_enemy(espr,ex,ey,ed,eframemax,eticks,eticksmax)

and then set:

 en.framemax = eframemax

in the function itself. You’ll see why we had en.frame = 0 in there now!

Next, we need to add some code to change which sprite to use, changing it every so many ticks so it animates like a flickbook. We already have variables for ticks and ticksmax to help with this.

In English, what we need to achieve is this:

  1. Count up ticks each loop of the game.
  2. If the ticks hit ticksmax, then change the sprite to the next frame.
  3. Restart the ticks counter.
  4. If the sprite frame passes framemax, set it back to the first frame.
  5. Draw the sprite.

The sprite to draw will be the “base” sprite (i.e. the first sprite of the animation, in this case sprite 17) plus the number of the frame. So 17+0 = 17, then 17+1 = 18, etc. looping back to 17+0. Clear? Great. Here’s the code, which needs to go in the en.draw() function within the make_enemy() function:

 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

Note that the spr() code has to change to add the frame number to the sprite number!

One last thing to make this work: You know those two calls to make_enemy()? One in _init() and one in _update()? Well, they both want to create enemies with sprite number 2. It’s 17 now, so we have to change that. Also, we have to include the new framemax parameter.

For the former, change it to:

make_enemy(17,120,120,0.05,3,0,5)

The “3” is the framemax – there are 4 frames numbered 0, 1, 2, 3, remember! The call in _update() needs to change similarly:

 make_enemy(17,rnd(128),rnd(128),0.05,3,0,4)

Save it, run it, and watch!

You can play with the “animation speed” using a different final value for ticksmax (the final parameter in the make_enemy() call) instead of 4. Or add more frames, and increase framemax appropriately.

Next time, we’ll look at animating the player too, but until then, here’s all the code so far:

function _init()
 -- player start location
 x = 64
 y = 64
 
 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(x,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 x > 0 then
  x -= 1
 end
 if btn(1) and x < 120 then
  x += 1
 end
 if btn(2) and y > 0 then
  y -= 1
 end
 if btn(3) and y < 120 then
  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
 spr(1,x,y) -- draw sprite 1 at x, y
 -- 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 x < this.x then this.x -= this.d end
  if x > this.x then this.x += this.d end
  if y < this.y then this.y -= this.d end
  if y > this.y then this.y += this.d end
 end
 
 add(enemies,en)
end

 

Leave a Reply

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