Gamedev Grievances #19: Attack Animations for Programmers

The dream of every game developer is to think of an idea, go ahead and implement it, and see it work first time. In reality, though, that dream is hardly ever realized, and instead programmers find themselves confronted with the nightmarish task of debugging and pulling code apart.

There are rare moments, however, when the dream does come to life. For me, this was in the form of a basic attack animation.

The Struggle

I’ve griped in severalpreviousposts about the programmer’s struggle to provide good graphics despite a lack of expertise in pixel art. I won’t go into that here again (much), except to reiterate that making your game look good is definitely important. Not only do graphics set the “feel” of the game, but they also shape a potential player’s initial perceptions of the game, and having good graphics can also motivate you as a developer to keep working on a project.

However, at the end of the day, programmers are programmers, which means that making good-looking art can be very difficult. As a programmer, I found that making enemy attack animations was especially a case in point. Not only do you have to make the enemy sprites look good in the first place, but you also have to make the sprites move, frame by frame, in a way that looks reasonably like a vicious attack.

But there’s an easier, more programmer-friendly way to make those animations. Yes, that’s right: why use art for your animations when you can use code?

Hop, Skip, Jump

Now, I’m definitely not saying that game developers should never work on improving their art skills – but sometimes it helps to play to your strengths when the option’s available to you. In this case, making a basic “jump-attack” animation using only code is quite easy (if you know some maths), and doesn’t need any pixel-art skill at all.

I found the inspiration for a jump-attack animation by watching some playthroughs of Shiren the Wanderer: The Tower of Fortune and the Dice of Fate. This game (and the Mystery Dungeon series in general) has a similar playstyle to Ambience, and it’s always useful to take a look at games of a similar genre to help gather and generate ideas. (Without stealing said ideas, of course!)

A random screenie from Shiren the Wanderer: The Tower of Fortune and the Dice of Fate.

So, I needed an enemy monster to jump at and attack the player. Triggering the animation was easy – I already had a basic “state” variable to alter a character’s sprites for walking, attacking, and standing still. The only question remaining was: how to actually make the animation itself using code?

I did consider using GameMaker’s paths system for this. In GameMaker, a “path” is a resource type which stores coordinates and forms a path between them; objects can then be made to move along that path at a certain speed as needed. However, I decided it wasn’t quite what I needed. For one thing, I would have to make eight paths for each of the eight directions, or rotate the path each time an enemy had to attack, which was cumbersome.

Then I realized that the motion path couldn’t simply be rotated for each direction. This was because the motion had two components: one component heading out towards the player and back (which was rotatable), and another, always-vertical component corresponding to the enemy monster jumping into the air. I couldn’t easily think of a way to get a monster object to move along two paths at the same time.

So I decided it was time to do some maths.

Time For Some Mathemagic!

As I said above, I needed two components of the motion making up the net attack path:

The timing of each component was different, too, and to deal with this I instituted a single “time variable” which plugged into two different equations, one for each motion.

The green, vertical “jump” component needed to look like the monster was jumping under gravity. This means that it needed to follow a “curved” pattern as the time variable changed, moving up, staying almost still, then coming back down – a bit like the path a ball makes through the air when you throw it. In the real world, this path (plot of height vs. time) is parabolic, but I cheated and used a segment of a sinusoidal curve instead. This was because the sine curve “hump” looked close enough to a parabola anyway, and more importantly, it also made it easier to get the monster to jump twice during the attack: once to jump out, the second time to jump back to the starting position.

The blue attack component heading out towards the player needed to be straight out and straight back, without any lingering at the ends – so I needed a “spiky” oscillating linear-like function. This I achieved using a combination of trig and inverse trig functions.

Finally, I added up the horizontal and vertical parts contributed by each component, and put the results into two position variables, one each for the X- and Y-position as a function of the time variable. Then I set the time variable to continually increment to get the attack animation going. (I also had to add some code to get the animation to only occur while the monster was actually attacking, but this was easy.)

This is what it looked like in the end:

The best thing about this was, as I said at the very beginning of this post, the animation worked the very first time, with no debugging required! That’s definitely a first for me, and I’ve been using GameMaker for about ten years! (In light of that, I’m not sure whether I can even call this post a gamedev “grievance” anymore…)

The Learning Curve

Here’s what I learned from this experience:

Play to your strengths. If coding is your thing, use it wherever possible instead of doing things you’re not so good at (like pixel art). You can extend this principle to other aspects of gamedev as well. For example, if meticulous level design isn’t your thing, maybe consider making procedurally generated areas. If you like maths, use it wherever possible to get things done. And so on.

Expect to debug. It’s very rare to get a system working first time, like this did. But when it happens, cherish the moment.