Over the past week I’ve spent most of my time working on animation and basic support for combat, along with a little time invested in performance optimizations to keep the game running well on the XBox.
My initial approach to animation for the player character was to use a single task that ran in the background, periodically updating the player’s animation frame and then going to sleep for a fraction of a second. It worked, but writing all of the player’s animation logic as a single task became cumbersome very quickly – I essentially was writing a very large, very complex state machine that also had to run concurrently with the rest of the game code. The idea of adding combat animations simply made this approach a non-option – it was too complicated.
As a result, I decided to toss the animation task I currently had and start fresh with a different approach. I ended up test-driving a very simple class designed for handling framerate-independent animation playback, called an Animator. The Animator‘s responsibility is to update the currently playing animation and handle transitions to new animations. Animations are written as generators, like the original animation task, but instead of using one giant generator for all the animations, or a generator for a set of animations, each individual animation is its own generator, only a few lines long.
Every frame, the Animator ‘steps’ the current animation’s generator repeatedly until it either suspends (returning an amount of time to suspend it for, so that the Animator knows when to wake it up next), or hands off control to a new animation. In the event that a new animation is selected, the Animator switches over to it and continues stepping it. This allows for complex animation logic to work correctly regardless of the current framerate, and makes it fairly simple to isolate the details of each animation from other animations. Having the Animator responsible for the current animation means that I can also easily interrupt an animation that’s in progress, even if the animation is suspended – something I couldn’t easily do when the entire animation system was a single task (mind you, it was possible with that approach – just a pain.)
The end result is that the player’s animations now look something like this:
protected Anim Animation_Walk_to_Stand () {
return Animation_OneShot(AnimId_Walk_to_Stand, 50 * Time.MillisecondInTicks)
.Chain(Animation_Stand);
}
protected Anim Animation_Backdash () {
var a = Animation_OneShot(AnimId_Backdash, 40 * Time.MillisecondInTicks)
.Chain(Animation_Stand);
using (Finally.Do(() => {
Backdashing = false;
Velocity.X = 0.0f;
}))
while (a.MoveNext())
yield return a.Current;
}
protected Anim Animation_Walk_Turn (int direction) {
return Animation_OneShot(AnimId_Walk_Reverse, 60 * Time.MillisecondInTicks)
.SwitchIf(Animation_Walk, () => Facing != direction)
.Chain(() => Animation_Wait()
.SwitchIf(Animation_Walk, () => Facing != direction)
);
}
While they’re still somewhat complicated, they individual animations are much more isolated from each other than they were previously, and it’s much easier to debug and fine-tune animations than it was before.
Functions like Animation_OneShot are predefined ‘animation templates’ that take a GroupID that specifies a set of frames to play, along with a rate at which to play them. The animation template is a generator that simply yields one frame at a time, followed by the specified delay, until the animation is complete. This has the nice advantage of being composable – you can add additional logic onto an existing animation by wrapping it with another generator that yields values from the inner generator after preprocessing them, like in the Animation_Backdash function above. This also gives you a way to express logic that should be executed at the beginning and end of an animation, even if the transition is abrupt (thanks to the IDisposable pattern).
The Chain and SwitchIf functions are extension methods that operate on animation generators by wrapping the generator with a new one and returning it.
Chain‘s behavior is fairly simple – at the end of the animation, control is transferred to a new one automatically, by evaluating a function passed in as a parameter. Passing the new animation in as a parameter allows dynamically choosing a new animation based on the current state of the game, or passing control off to an animation stored in a variable.
SwitchIf is slightly more complicated – it evaluates a condition (passed in as a function) on every step of the wrapped animation, and if the condition is true, it immediately switches to a new animation, without waiting for the current one to stop. This gives you an easy way to express immediate transitions between animations without having to manually detect state changes in your game code and explicitly force a transition. This is important for doing things like switching between a standing and walking animation based on the player’s velocity – you could explicitly call the Animator.SetAnimation method to switch to the walk animation, but doing that would require you to keep track of the player’s previous velocity and detect when he’s beginning to walk, which is a real hassle. Using SwitchIf allows you to express that logic like this instead:
protected Anim Animation_Stand () {
return Animation_PingPong(AnimId_Stand, 50 * Time.MillisecondInTicks)
.SwitchIf(Animation_Stand_to_Walk, () => Acceleration != 0)
.SwitchIf(Animation_Grapple, () => Grappling);
}
The main advantage here is that the SwitchIf clause is only evaluated while the player is currently standing, so you don’t need to keep track of any additional state. The primary downside to this approach is that it’s possible for the player’s animation to get into a ‘dead-end’ state, where there are no active SwitchIf clauses. In this case, the only way to restore the player’s animation to normal is to explicitly call Animator.SetAnimation – kind of annoying, but not any worse than the old approach. Luckily almost all my animation logic so far is easily expressed using switch conditions, so I rarely run into the dead-end problem.
Once I had this working, I went ahead and implemented a basic punching attack for the player character, along with a castlevania-style backwards dash move. Those two additions combined with the basic movement and jumping already implemented gives the player enough control to have fairly interesting fights with enemies (assuming that the enemies actually put up a fight, at least…)
The next step was to put something in that’s worth fighting with. Towards that end, I spent a few hours making lots of refactorings to my game code – pulling logic for things like movement, animation, collision detection, and so on out of the Player class and into a new base class called Entity. This required generalizing some of my existing code, and I had to fix a couple bugs I hadn’t noticed along the way, but when I was done the player’s movement code ended up a lot simpler and I was able to create a monster entity with a few dozen lines of code.
I created a new Monster class derived from Entity and added some basic AI to it: the monster walks forward in a given direction until it encounters the player or encounters an obstacle. When it encounters the player, it stands still and attempts to hit the player with its sword until the player is gone. When it encounters an obstacle, it reverses direction. Not particularly challenging to fight, but enough to be worth testing out.
One of the main details I had to tackle was how to detect a collision between the monster’s sword and the player, or the player’s fist and the monster – what I ended up doing was adding an AttackShape field to the Entity class that can optionally contain a polygon. If the field contains a polygon, every frame it will automatically be checked against other entities in the game for collision, and if a collision occurs, the entity is added to a temporary list and an ‘attack landed’ event is fired. The temporary list allows me to avoid dealing damage to an entity multiple times with one attack, so that all you have to do is hit your target with any part of your attack animation. Using a separate shape for an entity’s attack turns out to be a good idea, because it makes it straightforward to create entities that don’t deal damage on contact, which gives the player a lot more freedom when fighting by allowing them to focus on dodging actual attacks instead of having to try and avoid the creature’s hitbox.
After I had the collision detection for attacks working, I had to spend a little time rigging up the attack shapes to roughly match the player and monster’s attack animations so things would look fairly convincing.
The end result is that the player can now fight with a sword-wielding skeleton in the test level. Both of them are able to take damage from attacks and deal damage with attacks of their own. The skeleton can traverse the level fairly effectively, walking up and down slopes and dropping off ledges as necessary, which is pretty decent for a ‘filler’ monster. More advanced monsters will need actual pathfinding, and logic for chasing after the player.
The player’s health also has some simple logic implemented now for regeneration – health passively regenerates at a fixed rate, but taking damage from any source will cause your regeneration to stop, and then slowly resume after a short time. This means that your health won’t regenerate in between strikes if you’re being attacked repeatedly by a monster, but if you’re near death and in combat, dodging attacks for long enough will allow you to begin to regenerate. Having fairly rapid health regeneration is important for achieving the kind of combat I’m after – fights with small numbers of extremely dangerous enemies that require caution and rhythmic timing of attacks and dodges, instead of fights that involve plowing through dozens of skeletons or zombies like in some of the Castlevania games.
After all this, I started to have some severe performance issues – the logic in the Player class for handling sloped surfaces and motion had always been fairly high in my profiler stats, and now that I was running all that logic twice (once for the player, once for the monster) it was accounting for nearly half my processor usage on the PC, and was bringing the XBox version of the game down to the point where it could no longer consistently hit 60 frames per second – not good at all.
I ended up taking two steps to address the problem:
The first step was to solve an issue I had been anticipating to begin with – I was storing all the level’s geometry in a single List container, which meant that every single collision detection operation had to scan the entire level, regardless of the location of the object being checked for collision. I solved this by creating a simple SpatialCollection type that could store a list of objects and group them by their general location, so that I could do efficient collision checks against only nearby objects, without having to write a bunch of code. It took me a couple hours to write a good set of unit tests for the container and get it working, and then another hour or so to integrate it into the game in place of the old List. Luckily I didn’t have to alter any serialization or level loading code – just the code that did the actual collision checks.
After that, I spent another hour or so running performance profiles of the game on my PC, and optimizing the obvious hotspots. There were a few obvious things, like calling the List.Count property getter hundreds of times in the body of a loop instead of once at the beginning of the loop, and then there were also less obvious things, like the fact that I was unnecessarily recomputing the bounding box of a polygon every time it changed position (stupid…). After I killed most of the obvious bottlenecks, I ended up with a profile that looked something like this:
One thing I had to learn the hard way was that the debugger and profiler both have a tendency to suppress JIT optimizations in .NET, like method inlining. This can cause some code (like the subtraction operator on vectors) to appear much higher in a profile than it would otherwise. Regardless, though, the top dozen or so items on a profile almost always contain one or two ‘low-hanging fruit’ that you can easily eliminate from the performance profile with some quick tuning and refactoring.
I even found that it was worthwhile to optimize out things like unnecessary calls to property getters, because they tended to account for a larger performance hit on the XBox than on the PC – probably due to the nature of the XBox’s JIT and processor (which differ greatly from a typical desktop environment). Taking a look at frequently accessed properties can also be helpful since they can occasionally contain code that performs actual computation – for example, the property getter for RuntimeGeometry.Anchor in the profile above accounts for more than 3% of total processor usage. As it happens, that property getter performs some floating point arithmetic to get the right value before returning it, and I was calling it repeatedly inside of a loop (assuming that it was a simple, inexpensive field access) – so optimizing out that call turned out to be a great decision.
Once I was done with my optimization work, I had dropped my average CPU usage on the PC from 25% to 8% – around 1/3 of what it was before, which brought my framerate back into much better territory, and got the game running well on the 360 again (currently at around 20% CPU usage). This leaves me plenty of room to add new gameplay logic and graphics code without having to optimize for a while.



