I spent some time this week building a new animation system to replace my old hard-coded player animations. Right now the player doesn’t look any different, but the new system is set up such that I can drop the animation frames my artist is working on directly into my Content folder and see them in action with minor XML changes, instead of having to adjust large parts of the Player class.

One of the main goals I had for the Player class was to separate the presentation-related aspects of the animations (number of frames, size, etc) from the logic-related ones. Previously, both were handled in the same C# function – to handle punching, I had an Animation_Punch function in the Player class that looked something like this:

        protected Anim Animation_Punch () {
            _AttackShape = new DrawablePolygon(
                new Vector2[4]
            );

            using (Finally.Do(() => { ActiveAttack = Attack.None; _AttackShape = null; }))
            using (var a = Animation_OneShot(AnimId_Punch, 40 * Time.MillisecondInTicks))
            while (a.MoveNext() && (ActiveAttack == Attack.Punch)) {
                float x = ObstructionWidth / 2.0f * Facing;
                float y = -ObstructionHeight + ArmTop;
                float w = ((Sprite[Animator.Group][Animator.Frame].Width * ScaleFactor) - ObstructionWidth) / 2.0f * Facing;
                float h = ArmHeight;

                _AttackShape.SetVertex(0, new Vector2(x, y));
                _AttackShape.SetVertex(1, new Vector2(x + w, y));
                _AttackShape.SetVertex(2, new Vector2(x + w, y + h));
                _AttackShape.SetVertex(3, new Vector2(x, y + h));

                yield return a.Current;
            }

            yield return new SetAnimation {
                Animation = (Jumping && !JumpLanded) ? Animation_Jump_Fall() : Animation_Stand()
            };
        }

As you can see, the presentation - Animation_OneShot(AnimId_Punch, 40 * Time.MillisecondInTicks) – and logic are mixed directly together. Also, it has hard-coded coordinates and sizes in it, in order to keep the collision detection in sync with the animation. My goal is to eliminate both such that the Animation_Punch function becomes pure logic and all of the presentation-related data lives in the animation file. The use of an animation file also eliminates annoyances like hard-coded animation IDs, replacing them with filenames.

The equivalent in the new animation file looks like this:

            <SpriteAnimation typeId="1">
                <Name>Punch</Name>
                <Group name="punch" />
                <Frames delay="40" />
                <NativeWrapper name="Animation_Punch" />
            </SpriteAnimation>

As you can see, it expresses the same information, though in some cases it is able to omit things that had to be explicit before. The Animation_Punch function still exists, but has a more clearly defined purpose:

        protected Anim Animation_Punch (Anim inner) {
            using (Finally.Do(() => { ActiveAttack = Attack.None; _AttackShape = null; }))
            using (inner) {
                ActiveAttack = Attack.Punch;
                _AttackShape = new DrawablePolygon(
                    new Vector2[4]
                );

                while (inner.MoveNext()) {
                    var group = (SpriteGroup)Animator.Group;
                    var frame = group[Animator.Frame];
                    float x = ObstructionWidth / 2.0f * Facing;
                    float y = -ObstructionHeight + ArmTop;
                    float w = ((frame.Width * ScaleFactor) - ObstructionWidth) / 2.0f * Facing;
                    float h = ArmHeight;

                    _AttackShape.SetVertex(0, new Vector2(x, y));
                    _AttackShape.SetVertex(1, new Vector2(x + w, y));
                    _AttackShape.SetVertex(2, new Vector2(x + w, y + h));
                    _AttackShape.SetVertex(3, new Vector2(x, y + h));

                    yield return inner.Current;
                }

                if (Jumping && !JumpLanded)
                    PlayAnimation("Jump_Falling");
                else
                    PlayAnimation("Stand");
            }
        }

Oddly enough, the function actually got larger. Future refactorings will enable me to shrink it, but for now, it at least has less presentation mixed in – the actual animation itself is passed in via a parameter (inner), and the function is called as a result of being referenced by the animation file, instead of the other way around. The logic for pulling out the current frame is also abstracted out, which is another small but important improvement.

One of the other requirements for my animation system is the ability to set up simple triggers that cause one animation to switch to another. The way I previously implemented this was by checking the triggers every frame, using a convenience function in my C#:

        protected Anim Animation_Stand () {
            return Animation_PingPong(AnimId_Stand, 50 * Time.MillisecondInTicks)
                .SwitchIf(Animation_Stand_to_Walk, () => Acceleration != 0)
                .SwitchIf(Animation_Grapple, () => Grappling);
        }

Now, the entire animation lives in the file, more or less unchanged:

        <SpriteAnimation typeId="1">
            <Name>Stand</Name>
            <Group name="stand" />
            <Frames delay="50" loop="PingPong" />
            <Branches>
                <Branch name="Stand_to_Walk" if="Acceleration != 0" />
                <Branch name="Grapple" if="Grappling" />
            </Branches>
        </SpriteAnimation>

One detail of note here is that this means I’ve moved the conditions from compiled code (C#) to interpreted code (they’re now handled using the same expression evaluator I already use for configuring levels). This has a performance impact, but is worth it since it means I’m free to reload animations without having to recompile the game – particularly important if I’m trying to quickly fine-tune animations, since it cuts out the entire ‘Save, Exit, Compile, Start, Load’ cycle.

Thanks to the fact that my animations were already represented as composited iterators (take an iterator that represents an animation, and then layer another iterator atop it that will play a second animation when the first one completes – and so on), the core animation code didn’t have to change at all, which was a big time saver – when I switched from the old C# animations to the new file-based ones, the animations all looked the same, so I didn’t have to spend any time hunting down minor differences.

One other advantage to this approach is that it’s now a reasonable idea for me to let my artist make changes to the game’s animations, instead of doing it all myself. Even fairly complex animations are understandable:

        <SpriteAnimation typeId="1">
            <Name>Jump_Falling</Name>
            <Group name="jump_fall" />
            <Frames delay="70" first="-2" last="-1" loop="Repeat" />
            <Branches>
                <Branch name="Jump_to_Walk" if="(Running) and (!Jumping)" />
                <Branch name="Jump_to_Stand" if="(!Jumping)" />
                <Branch name="Grapple" if="Grappling" />
            </Branches>
        </SpriteAnimation>

As long as you understand XML, most of it is fairly easy to interpret. The equivalent C# would have been much harder to understand, and easier to break if my artist had to edit it to make changes.

The next thing I did was spend some time getting saved games working properly again. The addition of two-character support broke some parts of my saved game implementation, and it seemed like it was about time to fix them. The core problem ended up being larger than I expected – properly loading a saved game without a lot of hacks meant that I had to rethink the way I structured some of my game code.

Originally, objects like entities and geometry had both design-time and run-time representations. For entities, these were the LevelEntity and Entity objects. You constructed a LevelEntity with basic configuration information on the entity, and then added it to a Level. At runtime, the game would load the Level, and construct a RuntimeLevel object from it. Every LevelEntity would become a corresponding Entity in the RuntimeLevel. The core problem here was that constructing an Entity took a reference to the Game object, not to the Level or RuntimeLevel. So if I wanted to construct a new version of an entity (like a monster, or a player), I had no way to tell it which Level to use – it had to guess.

The first step I took was to rename Entity to RuntimeEntity, because it was getting pretty confusing. :) After that, I bit the bullet and changed everything such that constructing a RuntimeEntity required a RuntimeLevel instead of a Game. This meant I had to shuffle lots of things around, and update some code, but in the end, the payoff was worth it – now, I could load a new level from disk, construct all the entities and geometry, and swap it in for the current level, without any glitches. This was an important requirement for being able to undo actions like destroying walls, killing monsters, etc when the player loaded a saved game.

This change also helps kill off some technical debt I’d accrued in the design – lots of code in various places was jumping through ridiculous hoops, doing things like this.Player.Game.RuntimeLevel.Level.Entities[...]… in order to get at objects, so I clearly needed to rethink my dependencies.

One of my next goals is to add a way to place named ‘markers’ inside my animations, so that the game logic can reference locations like the player’s left hand without having to do arithmetic every frame. Getting that working will go a long way towards shrinking the size of functions like Animation_Punch and allow me to add more detailed animations for things like footsteps and attacks.