Useful state machines for XNA development

As I continue to implement various mechanics of the game, I keep finding common patterns for state management in game entities. For example, lots of entities sometimes need to flash on and off at regular intervals. Others need to slowly fade away over time. These needs keep popping up. So how do I share the code to make it happen?

Inheritance in this setting, even when it’s logical and possible, turns into an absolute mess pretty quickly, as every useful function gets crammed into the base class with lots of special cases in subclasses. Static class methods will work sometimes, especially when the thing you’re trying to do is essentially functional. But what about when there’s actual per-instance state you need to track?

To solve this, I’ve created several XNA-specific state machines to do simple things on behalf of other classes. They get fed a GameTime every Update() cycle, and update their state accordingly. The concept is most easily illustrated with an example. Here’s how I implement a simple Timer:

/// 
/// A timer that will tell you when the time has elapsed.
/// 
public class Timer {

    private double _timeTillAlarmMs;

    public Timer(double timeTillAlarmMs) {
        _timeTillAlarmMs = timeTillAlarmMs;
    }

    public void Update(GameTime gameTime) {
        _timeTillAlarmMs -= gameTime.ElapsedGameTime.TotalMilliseconds;
    }

    public double TimeLeft { get { return _timeTillAlarmMs; } }

    public bool IsTimeUp() {
        return _timeTillAlarmMs <= 0;
    }
}

Lots of game entities have a timeToLive member that's implemented in this fashion, but I get quite a bit of traction out of it in surprising places.

Next up is an oscillator, which cycles on or off in a period of your choosing:

/// 
/// Simple oscillator between states at a time interval of your choosing
/// 
public class Oscillator {

    private readonly double _periodMs;
    private double _timer;

    public Oscillator(double periodMs, bool initialState) {
        _periodMs = periodMs;
        IsActiveState = initialState;
        _timer = periodMs;
    }

    public bool IsActiveState { get; private set; }

    public void Update(GameTime gameTime) {
        _timer += gameTime.ElapsedGameTime.TotalMilliseconds;
        if ( _timer >= _periodMs ) {
            IsActiveState = !IsActiveState;
            _timer %= _periodMs;
        }
    }
}

This is great for a "flash on and off" effect, also very common. But it's really just the 2-state case of more general state machine that counts up to a certain total over and over:

/// 
/// Counter state machine. Counts up to a given value, then back to zero.
/// 
public class Counter {
        
    private readonly double _periodMs;
    private readonly int _numStates;
    private double _timer;

    public Counter(double periodMs, int numStates) {
        _periodMs = periodMs;
        StateNumber = 0;
        _timer = periodMs;
        _numStates = numStates;
    }

    public int StateNumber { get; private set; }

    public void Update(GameTime gameTime) {
        _timer += gameTime.ElapsedGameTime.TotalMilliseconds;
        if ( _timer >= _periodMs ) {
            _timer %= _periodMs;
            StateNumber = (StateNumber + 1) % _numStates;
        }
    }
}

These counters are super handy for keeping track of which frame of animation a game entity is on.

What's great about all these little devices is that they're totally reusable in any context, rather than wired into a hierarchy of classes. These are the ones you'll probably find most useful, but there are many other, more complicated ones I've built. For example, this one helps game entities determine the question of whether they are standing on solid ground. This should be an easy question to answer, except that Box2D (or maybe just Farseer) doesn't always handle collisions and contacts correctly when Body objects are created and disposed.

/// 
/// Class to help track whether an entity is in contact with the ground or ceiling
/// 
public class StandingMonitor {
        
    public bool IsStanding { get; set; }
    public bool IsTouchingCeiling { get; set; }
    public int IgnoreStandingUpdatesNextNumFrames { get; set; }

    /// 
    /// Updates bookkeeping counters
    /// 
    public void UpdateCounters() {
        if ( IgnoreStandingUpdatesNextNumFrames > 0 ) {
            IgnoreStandingUpdatesNextNumFrames--;
        }
    }

    /// 
    /// Updates the standing and ceiling status using the body's current contacts, 
    /// given the location of its lowest point.
    /// 
    public void UpdateStanding(Body body, World world, Vector2 standingLocation, float width) {

        bool isStanding = false;
        bool isTouchingCeiling = false;

        var contactEdge = body.ContactList;
        while ( contactEdge != null ) {
            if ( !contactEdge.Contact.FixtureA.IsSensor && !contactEdge.Contact.FixtureB.IsSensor &&
                    (contactEdge.Contact.IsTouching() &&
                    (contactEdge.Other.GetUserData().IsTerrain || contactEdge.Other.GetUserData().IsDoor)) ) {
                Vector2 normal = contactEdge.Contact.GetPlayerNormal(body);
                if ( normal.Y < -.8 ) {
                    isStanding = true;
                } else if ( normal.Y > .8 ) {
                    isTouchingCeiling = true;
                }
            }
            contactEdge = contactEdge.Next;
        }

        /*
            * If we didn't find any contact points, it could mean that it's because Box2d isn't playing nicely with
            * a newly created body (as when a tile reappears).  In that case, try to find the ground under our feet
            * with a ray cast.
            */
        float delta = .1f;
        if ( !isStanding ) {
            foreach ( Vector2 start in new Vector2[] {
                standingLocation + new Vector2(-width / 2, -delta),
                standingLocation + new Vector2(width / 2, -delta),
            } ) {
                world.RayCast((fixture, point, normal, fraction) => {
                    if ( fixture.GetUserData().IsTerrain ) {
                        isStanding = true;
                        return 0;
                    }
                    return -1;
                }, start, start + new Vector2(0, 2 * delta));
            }
        }

        if ( IgnoreStandingUpdatesNextNumFrames <= 0 ) {
            IsStanding = isStanding;
        }

        IsTouchingCeiling = isTouchingCeiling;
    }
}

As for the IgnoreStandingUpdatesNextNumFrames field, I'll let you imagine for yourself the horrors that necessitated that fearsome kludge. Did I mention Box2D doesn't handle resizing Body's very well either?

Prototyping the hover boots

I’m on a roll with new collectible power ups! I just finished implementing the hover boots, which you can see in action below:

The basic idea of the hover boots is that they allow you to hover briefly in mid-jump or mid-fall. You can only do this once per jump, and it doesn’t last very long. You can also cancel it early for fine control of the effect, and eventually there will be a couple upgrades that make the effect last longer and then as long as you want. Being able to halt in mid air not only makes movement more fun (it really increases your jump distance, but not height), it engenders a bunch of interesting exploration / puzzle possibilities.

I have to confess that I got the idea for this item from the Gargoyle’s Quest series, where it was used really effectively:

Confusingly, the third Gargoyle’s Quest game was called Demon’s Crest, and it’s a relatively unknown sleeper hit for the SNES. Although not really a game about exploration, it has quite a few Metroidvania-like aspects on top of being a great action game. It’s worth a play through, or if you’re lazy you can just watch a speed run of it:

The next power-up ability I’m working on is also a total rip off from other great games. If you think you know what it is, feel free to guess in the comments!

Demoing the pulse width modulator

Early on in the project, I had this crazy idea for a gun modification I thought would be fun to use, and finally finished implementing it today. Basically it’s a cross between a charge shot and rapid fire, with manual adjustments to make it more charge-like or more rapid-fire-like in steps. I’ve been calling it a pulse width modulator with the full understanding that that’s not what those words mean. Anyway, after playing around with it for most of a day I’m convinced it’s fun to use, probably more so than any single element of the game so far. Here I am noodling around with it, as well as demonstrating several new sound effects I created.

Watching this reminds me that I need to globally limit the number of shattered block particles to keep the frame rate up.

The gun has four settings you select by pressing up and down on the D-Pad. Then you hold down the fire button to shoot. As you can see in the video, the lowest setting is a classic charge weapon, and the highest is auto rapid fire. In between the highest and lowest settings, holding the button down results in periodic shots with power roughly proportional to how charged up it is. All the auto-fire modes do roughly the same damage per second, assuming all your shots hit, so this is a power-up that’s more about tactical flexibility and puzzle solving options than brute strength. I do think I’ll seriously increase the power of the first two rapid fire settings – there’s not a ton of reason to use them yet. The setting just below full-auto is interesting as well, because it shoots normal shots not quite as fast as a dedicated player could push the button, but with much less effort. I’m guessing I’ll use that one a lot in my playthroughs.

I also overhauled how the overlay elements (health bar, weapon selection, etc.) are drawn onto the screen. Some of these, especially the mini-map, are actually kind of expensive to draw, so I made some changes to the code to draw all the elements to a texture that I can re-use until something changes. It was getting to be an issue already, and who knows how much more info I’m going to shove up there.

Shockingly inefficient to produce

Shockingly inefficient to produce

For all you XNA devs, I discovered a frustrating issue while I was implementing this change, where the screen would flash purple (the default “transparent” color) for one frame whenever I updated the overlay texture. After a lot of debugging, I figured out that calling GraphicsDevice.Clear() on a screen-sized RenderTarget2D was overwriting the entire screen with purple for that draw frame. This didn’t seem like it should be happening — the clear() operation should work on the RenderTarget, not the back buffer itself — but it was. I fixed it by drawing the texture at the very beginning of the game’s Draw() cycle, then drawing the texture itself when it was time to draw the overlay. This means that for this kind of performance enhancement, you have to split it into two parts, like so:

public override void Draw(SpriteBatch spriteBatch) {
    spriteBatch.Begin();
    spriteBatch.Draw(_screenOverlay, _screenRectangle, Color.White);
    spriteBatch.End();
}

public void PrepareDraw(SpriteBatch spriteBatch) {
    if ( _screenOverlay == null || _needsRedraw ) {
        GraphicsDevice graphics = spriteBatch.GraphicsDevice;
        PresentationParameters pp = graphics.PresentationParameters;
        _screenRectangle = new Rectangle(0, 0, pp.BackBufferWidth, pp.BackBufferHeight);
        _screenOverlay = new RenderTarget2D(graphics, pp.BackBufferWidth, pp.BackBufferHeight);

        graphics.SetRenderTarget(_screenOverlay);
        graphics.Clear(Color.Transparent);
        spriteBatch.Begin();
        _overlayElements.ForEach(element => element.Draw(spriteBatch));
        spriteBatch.End();
        graphics.SetRenderTarget(null);

        _needsRedraw = false;
    }
}

It seems like about half the time I try to do something non-trivial with the XNA graphics framework, I get burned and spend hours debugging. I wonder if another framework would be any easier…

Fully working holocube

I got the holocube fully working, or as fully working as any feature can be at this early stage of development. Check it out!

There’s now a limit of a single holocube that can be placed at once, and if you place another one the old one shatters. The beam also now knows not to place a cube illegally, where it would overlap with the player or another on-screen entity, and the beam turns red in those circumstances.

I’m currently working on the next collectible upgrade, which is kind of a cross between a rapid fire and a charge shot. Here’s a little preview of me fooling around with firing a shot every single frame. Turns out that’s way too many, but it looks kind of neat.

I’m a little further along with the rapid-fire weapon modifier since I recorded that video, and so far I really like how it’s working out. More on that tomorrow!

Testing the holocube

I’ve been out of town a lot lately, which has been a lot of fun but hasn’t helped me make progress on the game. But I have been chipping away at it! Here’s a progress video update to prove it:

As you can see, the holocube beam now projects from the gun barrel and can be fired, creating a block you can step on. The current plan is to limit the player to one block at a time, so that when you fire a second one the first disappears, but that’s yet to be implemented. At the end, you can see me playing with a bug, having to do with Box2D not dealing well with collisions or separations when bodies are created or destroyed, which means that the player thinks he’s still standing even though he’s in the air. I already had to fix this for the normal terrain, so I know how to fix it for this.

You might also notice that I added a secondary weapon selection UI, as well as overhauled the health bar to make it simpler. Both may shrink in the final cut.

Prototyping the holocube

True story: I had plans for a power-up that lets you place a tile anywhere you want in the game world, hardly an original idea, before I bought and played Antichamber, a game that explores this mechanic to it fullest. And now I feel like a hack.

Nevertheless, I whipped up a quick prototype of how this tool will project from the player’s weapon to place itself in the game world. Have a look:

As Antichamber explored, being able to alter the landscape in this simple way introduces a huge number of possibilities for puzzle-like lock-and-key mechanics, especially when combined with other tools or weapons or other dynamic game elements. But the simplest use of this thing, which I will be working on first, is just as a stepping stool to let you jump one tile higher, or to allow you to scoot your way into a one-tile-high tunnel that isn’t on the ground — there’s no equivalent of a bomb jump planned, so this will be an important skill to use.

No more pixel counting for a while

I finally finished manually correcting the shot placement for every possible player animation, so that the beam weapon bobs along with the player as he moves. It was a lot of work, but on the whole I’m convinced it was worthwhile. The weapon’s beam now follows the character’s movements in a way that looks convincingly organic. Have a look!

Near the end, you can also see a debug mode I created just for this purpose, where the frame number of the character’s current animation is drawn on top of his base image. Then I just had to manually tweak the shot placement for each frame of each animation, note the appropriate offset on a legal pad, and repeat until done. Certainly it’s tedious, but less error prone than trying to count the pixels in the raw animation images and quite a bit faster. And I’m now done!

That means I get to move on to the next secret feature of the game, which we will discuss more tomorrow.