game mechanics:
Animate Sprite


pixelart icon


How to Animate a Sprite!


What is a sprite?

Here are the sprites we use in this example animation cartridge.

pixelart soldier sprites

You can copy these sprites or make your own! Make sure you draw each of these sprites in the exact same place we do, because the code will look for their sprite number. But once you understand the code, you can draw as many sprites as you want, where ever you want!


This is where you will want to draw your sprites in the PICO-8 sprite editor.

pico-8 spritesheet

(We leave the top left square (Sprite #0) as an X because that is a default empty tile in the map editor.)



pixelart icon


What is a variable?

The first code we write at the top of the file are the variables. These help us set up the game and keep track of data.

When variables are set outside of any function, that means that they will be set before anything even starts running in the game. And that means any code written after these variables will be able to find and use them.

If you are using the _init() function, then that would be a good place to put these variables for each game object that you want to animate. Or if you are using tables to organize your game objects then these variables can be inside of the objects\' tables.

 What is a sprite?

sprite = 1
x = -8
y = 59
timing = 0.25

The first variable is named sprite. Then the equals sign tells the game to remember it with whatever we write next. sprite holds a number and that number is the sprite-number of the first sprite in the sprite sheet.

The next variables are named x and y. Together these tell the game where the sprite is on the screen.

pico-8 screen position

x = -8 and y = 59 because the screen has 128 pixels from left-to-right and top-to-bottom (# 0-127). The X is where the sprite will start on the X-axis (horizontally) and -8 will be just to the left of the visible screen. The Y is where the sprite will start on the Y-axis (vertically) and 59 places the sprite in the middle of the screen.

pico-8 sprite position

The next set of variables are for keeping track of the animation timing.

timing = 0.25

timing is for setting a delay time between each animation (sprite change). This needs to be a decimal between 0 and 1. Smaller decimal = slower animation. This is explained more in the update section below.



pixelart icon


function _draw()
  cls()

  spr(sprite, x, y)

  print(sprite)
end

_draw() runs after _update and draws to the game screen 30 times every second.

cls() clears the game screen.

spr() draws a sprite at X and Y coordinates on the screen.

spr(sprite, x, y) tells the SPR function to draw the sprite number at the X and Y variables we set above.


print(sprite) will write the sprite variable to the screen. It will show as the number as it counts up by timing so you can see how fast the animation is happening.



pixelart icon


function _update()
    --animate
    sprite += timing
    if sprite >= 5 then sprite = 1 end

    --move
    x += 1
    if x > 127 then x = -8 end
end

The function _update() makes changes to the game 30 times every second.

You can also use _update60() to double that speed to 60 times per second. But if you choose to do that, then the animation will move twice as fast, so you should cut the timing variable in half.


The first thing we will update is the sprite number to create the animation.

sprite += timing increases the sprite-number by the number you set in the timing variable.


Why should timing be a decimal between 0 and 1?

Because the very next sprite is 1 whole number away. If we add 1 every time _update() runs, then we will be changing the sprite way too fast. Try setting timing to 1 and run the game to see just how fast 30 times a second is. And obviously, if we add 0, then the sprite number will never change. So it has to be a decimal between those two.

Let\'s say we use 0.5 for our timing number. That means 30 times a second (every time _update() runs) the sprite number will increase halfway towards the next sprite. And that means instead of changing the sprite 30 times every second, it will change the sprite 15 times every second. That is how we are controlling the timing with this number.

So play around with this number to get the animation timing how you\'d like it. The closer to 0 you make it, will slow down the animation. The closer to 1 you make it, will speed up the animation.

pixelart icon

To make this work, you must draw your sprites next to each other in a row and slightly change each drawing, and that will create the look of one character being animated.

pixelart sprite animation loop

Now that we are increasing the sprite number, what happens when it gets to the end of our character\'s sprites?

Well that means we need to restart the animation loop. So let\'s check when the sprite number gets to the end, and reset it to the start. It\'s as simple as this one line of code:

if sprite >= 5 then sprite = 1 end

Why 5 you ask?

Don\'t forget that we are adding a decimal to the current sprite number. So the sprite starts at 1. In this example, our timing variable is set to 0.25.  That means our code will basically count up by .25 every tick of the game.

Let\'s do it: 1 ... 1.25 ... 1.5 ... 1.75 ... 2 ... 2.25 ... and so on.

Now the thing you need to know is that PICO-8 will always round down to the lower whole number when trying to draw a sprite. So while the sprite number is anywhere between 1 and 2, then it will only draw sprite number 1. Each tick of the game that the sprite number is between two whole numbers, nothing is actually changing on the screen. So sprite number 2 doesn\'t actually get drawn until the sprite number reaches 2 or more.

Alright, let\'s keep counting: 3 ... 3.25 ... 3.5 ... 3.75 ... 4 ...

So now it gets to our last sprite, number 4. But if we reset it back to sprite 1 at this point, then we are only allowing sprite 4 to be on the screen for a split second. We actually want to allow sprite 4 to be drawn to the screen for the same length of time that the others were drawn. To do that we need to let it keep counting past 4.

4 ... 4.25 ... 4.5 ... 4.75 ... 5

Now we can see that we are letting it count up to 5, which leaves sprite 4 on the screen for the correct amount of time. However, we can\'t let the sprite number reach 5 or more, becuase then it will draw sprite number 5. For this example, sprite 5 is empty, or in your game it could be a totally different character or item and we don\'t want that to flash on the screen at all.

That\'s why we check if the sprite number is more than or equal to 5: if sprite >= 5


pixelart sprite animation loop

pixelart icon


The next thing we will update about this sprite animation is the position.

x += 1 will increase the X position by 1 pixel, making the sprite drawn 1 pixel to the right.


if x > 127 then x = -8 end will check if the sprite moved too far to the right and off the screen. And if that happens, then x = -8 will reset the sprite\'s X position to where it started.

Together, this code will simply start the sprite on the far left, make it move to the right, and then warp it back to the left side to infinitely walk across the screen.



pixelart icon




sprite = 1
x = -8
y = 59
timing = 0.25

function _draw()
    cls()

    spr(sprite, x, y)

    print(sprite)
end

function _update()
    --animate
    sprite += timing
    if sprite >= 5 then sprite = 1 end

    --move
    x += 1
    if x > 127 then x = -8 end
end


pixelart icon


 

Warning: If you are new to coding, this may look complicated. It uses tables to organize all of the different game objects. So if you have not used tables before, we suggest reading about tables in our Guide to help understand this.

What is a table?
function _init()
    --create a table for player data
    player = {
        sprite = 1,
        x = -8,
        y = 59,
        timing = 0.25,
    }

    --create an empty table for enemies data
    enemies = {}
    --set enemy sprites, positions, timing and first and last sprite frames
    enemy1 = { 
        sprite=5,
        x = -20, 
        y = 5, 
        timing = 0.1, 
        speed = 1.25, 
        first = 5, 
        last = 9 
    }
    enemy2 = { 
        sprite=9 , 
        x = -14, 
        y = 30, 
        timing = 0.2, 
        speed = 0.4, 
        first = 9, 
        last = 13 
    }
    enemy3 = { 
        sprite=13 , 
        x = -11, 
        y = 90, 
        timing = 0.4, 
        speed = 0.75, 
        first = 13, 
        last = 17 
    }
    --add each enemy to enemies table
    add(enemies,enemy1)
    add(enemies,enemy2)
    add(enemies,enemy3)

    --create an empty table for items data
    items = {}
    --set item sprites, positions, timing and first and last sprite frames
    item1 = { 
        sprite=48 , 
        x = 30, 
        y = 110, 
        timing = 0.3, 
        first = 48, 
        last = 56 
    }
    item2 = { 
        sprite=56 , 
        x = 60, 
        y = 110, 
        timing = 0.25, 
        first = 56, 
        last = 60 
    }
    item3 = { 
        sprite=60 , 
        x = 90, 
        y = 110, 
        timing = 0.15, 
        first = 60, 
        last = 64 
    }
    --add each enemy to enemies table
    add(items,item1)
    add(items,item2)
    add(items,item3)
end

function _draw()
    cls()

    --draw player
    spr(player.sprite, player.x, player.y)

    --draw enemies
    for enemy in all(enemies) do
        spr(enemy.sprite, enemy.x, enemy.y)
    end

    --draw items
    for item in all(items) do
        spr(item.sprite, item.x, item.y)
    end
end

function _update()
    --animate player
    player.sprite += player.timing
    if player.sprite >= 5 then player.sprite = 1 end
    --move player
    player.x+=1
    if player.x > 127 then player.x=-8 end

    --enemies
    for enemy in all(enemies) do
        --animate
        enemy.sprite += enemy.timing
        if enemy.sprite >= enemy.last then 
            enemy.sprite = enemy.first 
        end
        --move
        enemy.x += enemy.speed
        if enemy.x > 127 then 
            enemy.x=-8 
        end
    end

    --items
    for item in all(items) do
        --animate
        item.sprite += item.timing
        if item.sprite >= item.last then 
            item.sprite = item.first 
        end
        --don\'t move
    end
end

Run


pixelart pico-8 animate sprites

Here is what we drew in our sprite sheet. The same player as before, then 3 enemies, and 3 items.

pico-8 spritesheets

Notice that they all use only 4 sprites to create their animation except for the coin which uses 8.


We separate the player, enemies, and items into 3 different tables. For the player, we simply wrote the player data directly into the table. But the enemies and items table will hold smaller tables for each enemy\'s data and each item\'s data. So we wrote out those tables for each enemy as enemy1, enemy2, enemy3, and each item as item1, item2, item3.

You may notice some new variables stored with those objects. After timing, we also added speed to the enemies to allow each enemy to move at different speeds. Then we added first to remember the starting sprite number and last to remember to last sprite number to be used in the checks that happen in the _update() function.

The _draw() and _update() functions use a special table loop.

for entry in all(table)

That will cycle through each entry in a table and run the same code over them. This keeps our code nice and short. The only hard part is imagining what the tables look like and understanding how to prepare the table data for loops like this.


Save this cartridge image and open in your PICO-8 to get the sprites and the code so you can start playing around with it.



pico-8 sprite animation game cart




pixelart icon

4372

11 Jun 2020

Font