In this tutorial we'll create a walking character using sprites. This is the basis of many adventure and rouge-like games.
We could draw the sprites ourselves, but for simplicity I've downloaded a public domain sheet from the internet. This contains three animation phases in every line, and has one line per all 4 directions. There are lots of spritesheets on the net like this, because this is the popular RPG Maker's sprite layout.
WARN: Always check the licensing terms when you use assets downloaded from the internet. Do not use the asset if you're unsure about its terms of use.
Start meg4
and drag'n'drop this downloaded PNG image on the window to import.
The imported sprite sheet
As you can see, one character sprite is made up of 4 x 4 sprites. Let's display that! Press F2 to go to the [Code Editor].
We start with the usual skeleton. We know from the previous tutorial that we'll have to clear the screen and display the sprites
using [spr] in the loop()
function, because animation requires constant redrawing.
#!c
void setup()
{
/* Things to do on startup */
}
void loop()
{
/* Things to run for every frame, at 60 FPS */
<hm> cls(0);
spr(144, 84, 0, 4, 4, 0, 0);</hm>
}
The centre of the screen is at 160, 100 but our character is 4 x 4 sprites in size (32 x 32 pixels), so we have to subtract the
half of that. Then comes 0 for sprite
, meaning the first sprite, followed by 4, 4 because we want to display that many sprites.
The last two parameters are 0, 0 because we don't want to scale nor transform, we want the sprites to be displayed exactly as they
appear in the [Sprite Editor].
Next, let's allow changing the direction in which the character is pointing to. For that, we'll use [getpad], which returns the gamepad's state. The primary gamepad controller is mapped to the keyboard, so pressing the cursor arrow keys will work too. To handle the direction, we'll need a variable to store the current direction, and this should select the sprite we draw.
Get the sprite id
You can press F3 and click on the top left sprite of the character frame to get the sprite id for that
direction. We set these ids in the dir
variable, and then we'll use this variable in place of the sprite
parameter.
#!c
<hl>int dir;</hl>
void setup()
{
/* Things to do on startup */
}
void loop()
{
/* Get user input */
<hm> if(getpad(0, BTN_D)) {
dir = 0;
} else
if(getpad(0, BTN_L)) {
dir = 128;
} else
if(getpad(0, BTN_R)) {
dir = 256;
} else
if(getpad(0, BTN_U)) {
dir = 384;
}</hm>
/* Display the character */
cls(0);
spr(144, 84, <hl>dir</hl>, 4, 4, 0, 0);
}
Try it out! You'll see that by pressing the arrows our character will change directions.
Our character doesn't walk yet. Let's fix it! We want our character to walk when a button (or arrow key) is pressed, and stop when that's released. For that, we'll need a variable to keep track if the button is currently pressed.
#!c
int dir<hl>, pressed</hl>;
void setup()
{
/* Things to do on startup */
}
void loop()
{
/* Get user input */
<hl>pressed = 0;</hl>
if(getpad(0, BTN_D)) {
dir = 0; <hl>pressed = 1;</hl>
} else
if(getpad(0, BTN_L)) {
dir = 128; <hl>pressed = 1;</hl>
} else
if(getpad(0, BTN_R)) {
dir = 256; <hl>pressed = 1;</hl>
} else
if(getpad(0, BTN_U)) {
dir = 384; <hl>pressed = 1;</hl>
}
/* Display the character */
cls(0);
spr(144, 84, dir, 4, 4, 0, 0);
}
First, we clear the pressed
variable. Then in the if blocks we set it to 1. This way when we press a button, the variable
becomes 1, but as soon as we release the button, it will be cleared to 0.
We'll also need a variable to tell which animation frame to display. We could have used some funky expression to get the sprite id, but it is a lot easier to use an array instead storing which sprite to pick in the row. So row tells the direction, and columns tells the animation frame. Adding this two together gives us the final frame.
One more thing, we have three animation sprites, but we'll have to display four frames, the sprite in the middle needs to be displayed twice to get a proper back and forth animation for moving the legs. So in a given row we take the frame from the middle, the last, the middle again and then the first.
#!c
int dir, pressed<hl>, frame</hl>;
<hl>int anim[4] = { 4, 8, 4, 0 };</hl>
void setup()
{
/* Things to do on startup */
}
void loop()
{
/* Get user input */
pressed = 0;
if(getpad(0, BTN_D)) {
dir = 0; pressed = 1;
} else
if(getpad(0, BTN_L)) {
dir = 128; pressed = 1;
} else
if(getpad(0, BTN_R)) {
dir = 256; pressed = 1;
} else
if(getpad(0, BTN_U)) {
dir = 384; pressed = 1;
}
/* Display the character */
<hl>frame = pressed ? (frame + 1) & 3 : 0;</hl>
cls(0);
spr(144, 84, dir <hl>+ anim[frame]</hl>, 4, 4, 0, 0);
}
Next, we calculate which animation frame to display, but only if a button is pressed. If not, then we use a constant 0, meaning
the first frame. Otherwise we increase frame
to get the next frame, and we use bitwise AND to avoid overflow. When frame
becomes 4 (which is 0b100 in binary) and we AND that with 3 (0b011), then the result will be 0, so the frame counter wraps around.
We could have used "modulo number of frames" as well, but this is faster. Finally, we get the sprite id offset for this animation
frame (stored in the anim
array) and we add that to the dir
variable to get which sprite to display.
Try it out, press Ctrl+R! It works fine, except our character is animated way too fast. That's because we
increase the frame
counter on every refresh, so 60 times per second. To fix this, we should get the ticks from the MMIO and
calculate the frame independently to the refresh rate. However ticks counter is in millisec, so we should divide it. If we would
divide that by 100, then we'd get 10 frames per second. We'll use shifting to the right 7 bits instead, which is equivalent of
diving by 128. So first, press F1, and click on [Misc]. We can see that the ticks counter is at address 0x4, and it
is 4 bytes long (so we'll have to use [ini]). Go back to the code and replace the frame calculation with this.
#!c
int dir, pressed, frame;
int anim[4] = { 4, 8, 4, 0 };
void setup()
{
/* Things to do on startup */
}
void loop()
{
/* Get user input */
pressed = 0;
if(getpad(0, BTN_D)) {
dir = 0; pressed = 1;
} else
if(getpad(0, BTN_L)) {
dir = 128; pressed = 1;
} else
if(getpad(0, BTN_R)) {
dir = 256; pressed = 1;
} else
if(getpad(0, BTN_U)) {
dir = 384; pressed = 1;
}
/* Display the character */
frame = pressed ? (<hl>ini(0x4) >> 7</hl>) & 3 : 0;
cls(0);
spr(144, 84, dir + anim[frame], 4, 4, 0, 0);
}
And we're done! We have a nicely walking character animation that we can control in our game. You could also move the character on screen by using variables for the x, y arguments, but it is very common in such games to move the map under the character in the opposite direction instead.