Animate an 8-directional sprite, part 2: Death Animation

Tom Cantwell
4 min readAug 13, 2020

This is a continuation of another blogpost about how to animate a spritesheet in javascript. Read part one here.

This is the animation we’re going to add to the spritesheet

Right now, we have a spritesheet with 8 rows, 1 for each direction of movement. We’re now going to add another row which has a death animation.

The new animation is highlighted at the bottom.

The new animation has 5 frames. The last frame is repeated to fill the empty spaces but we won’t be using those frames.

The first thing to do is add this last row to the constants for referencing it later. We’ll call this last row “DEATH”:

//rows of spritesheet
const SOUTH = 0;
const SOUTHEAST = 1;
const EAST = 2;
const NORTHEAST = 3;
const NORTH = 4;
const NORTHWEST = 5;
const WEST = 6;
const SOUTHWEST = 7;
const DEATH = 8;

We’ll need a couple new variables as well.

//whether the character is alive or dead
let deathState = false;
let reviveState = false;

The reviveState isn’t strictly necessary, but for this little project I want to animate a revival sequence. It is an undead skeleton, after all.

We’ll add a new event listener for if there’s a click within a certain radius of the character’s head. Fortunately, there’s already a variable, “hypotenuse”, which I’m using to determine distance from mouse to character. If the character is already dead, clicking will trigger the revive state to become true. If the character is alive, they will become dead and the current keyframe will be set back to 0.

//Listen for clicking
canvas.addEventListener('click', clickListener)
function clickListener(e) {
if (hypotenuse < SCALED_WIDTH) {
if (deathState) {
reviveState = true;
} else {
deathState = true;
currentLoopIndex = 0;
}
}
}

I added a couple of circles just to make it visually clear when you’re close enough to click with an effect, and when you’re close enough to make the character stop moving. This is very much optional, but if you want to draw some indicators like this, just put them within the draw loop but before the character is drawn.

if (hypotenuse < SCALED_WIDTH) {
ctx.beginPath();
ctx.arc(positionX+(SCALED_WIDTH/2), positionY+(SCALED_HEIGHT/8), SCALED_WIDTH, 0, 2 * Math.PI);
ctx.fillStyle = "#81B048";
ctx.fill();
}
if (hypotenuse < SCALED_WIDTH/4) {
ctx.beginPath();
ctx.arc(positionX+(SCALED_WIDTH/2), positionY+(SCALED_HEIGHT/8), SCALED_WIDTH/4, 0, 2 * Math.PI);
ctx.fillStyle = "#578B28";
ctx.fill();
}

For the code that switches the row of the spritesheet, I added a new case to check the death state as the first case to check so that I don’t trigger any other rows to appear unless the character is alive.

//switch row of spritesheet for proper direction
switch (true) {
case (deathState):
currentDirection = DEATH;
break;
case (angle <= 22.5 && angle > -22.5):
//east
currentDirection = EAST;
break;
case (angle <= 67.5 && angle > 22.5):
//southeast
currentDirection = SOUTHEAST;
break;
case (angle <= 112.5 && angle > 67.5):
//south
currentDirection = SOUTH;
break;
case (angle <= 157.5 && angle > 112.5):
//southwest
currentDirection = SOUTHWEST;
break;
case (angle <= -157.5 || angle > 157.5):
//west
currentDirection = WEST;
break;
case (angle <= -112.5 && angle > -157.5):
//northwest
currentDirection = NORTHWEST;
break;
case (angle <= -67.5 && angle > -112.5):
//north
currentDirection = NORTH;
break;
case (angle <= -22.5 && angle > -67.5):
//northeast
currentDirection = NORTHEAST;
break;
}

When the character is dead, I need them to stop moving, so I added another “or” statement to the cases for when the character should not be moving.

//character stops when touching mouse or when dead
switch(true) {
case (hypotenuse <= SCALED_WIDTH/4 || !mousePresent || deathState):
moving = false;
break;
case (hypotenuse > SCALED_WIDTH/4 && mousePresent):
moving = true;
break;
}

Finally, here’s where the magic happens! Before if the character was not moving, I just had to make sure the animation didn’t progress in keyframes. Now, if the character is dead but not revived, it triggers a death animation one time only. If you click again to trigger the revive state, the revive animation will run the death animation in reverse, and then set the death state and revive state back to false.

if (!moving) {
//dying
if (deathState && !reviveState) {
frameCount++;
if (frameCount >= FRAME_LIMIT/MOVEMENT_SPEED) {
frameCount = 0;
currentLoopIndex++;
if (currentLoopIndex >= 4) {
currentLoopIndex = 4;
}
}
//reviving
} else if (deathState && reviveState) {
frameCount++;
if (frameCount >= FRAME_LIMIT/MOVEMENT_SPEED) {
frameCount = 0;
currentLoopIndex--;
if (currentLoopIndex <= 0) {
currentLoopIndex = 0;
reviveState = false;
deathState = false;
}
}
//just not moving
} else {
currentLoopIndex = 0;
}
}

And now we have another way to interact with the character!

--

--