About zachm

A professional software developer on sabbatical, just trying to build the best game he can without any idea what he's doing.

That was suspiciously easy

Slope experiment continues apace. You can now run over little hills without being launched into the air. Take a look!

Accomplishing this was relatively straightforward. Basically, after each simulation step, you check to see if the character is in contact with the ground. If not, and there wasn’t a jump initiated, you probe underneath their feet with a ray cast, then adjust their position and velocity accordingly if there’s a slope underneath them. It looks like so:

// If we haven't jumped, stick to the ground unless it's a cliff
if ( _onGround && !_jumpInitiated && !_standingMonitor.IsStanding ) {
    float closestFraction = 1;
    Vector2 closestPoint = Vector2.Zero;
    Vector2 closestNormal = new Vector2();

    foreach (var start in new[] {
        GetStandingLocation() + new Vector2(Width / 2f, 0),
        GetStandingLocation() + new Vector2(-Width / 2f, 0),
    } ) {
        _world.RayCast((fixture, point, normal, fraction) => {
            if ( fixture.GetUserData().IsTerrain && fraction < closestFraction ) {
                closestFraction = fraction;
                closestPoint = point;
                closestNormal = normal;
            }
            return 1;
        }, start, start + new Vector2(0, .9f));
    }
    if ( closestPoint != Vector2.Zero ) {
        _body.Position = new Vector2(_body.Position.X, closestPoint.Y - Height / 2 - .01f);
        // tweak the velocity to match the normal of the stuck-to slope
        float speed = _body.LinearVelocity.Length();
        // The rotation won't work properly unless we flip the y axis
        closestNormal = new Vector2(closestNormal.X, -closestNormal.Y);
        Vector2 rotated = Vector2.Transform(closestNormal, Matrix.CreateRotationZ(_facingDirection == Direction.Left ? Projectile.PiOverTwo : -Projectile.PiOverTwo));
        _body.LinearVelocity = new Vector2(rotated.X, -rotated.Y) * speed;
    } else {
        _onGround = false;
        IsStanding = false;
    }
}

Sadly, it doesn't work 100% of the time. About one time in 10, if you're moving at absolutely top speed and you hit the downward slope just right coming off the table top, you'll still catch air. That's because the character has moved so far horizontally in one frame that the ground has fallen more than the probe distance (.9 tiles) away from their feet. But I think I can live with that for now and consider what I have so far a proof of concept.

Going full retard, touching the third rail, etc.

I really didn’t want it to come to this, but after playing Operation Smash I don’t see what choice I have in the matter. I’m talking, of course, about implementing slopes in my game. I whipped up a little test area to see what I was getting myself into, approximately. I don’t have any slopey tiles yet, so I’m just placing a ramp object into the physics engine and turning on debug mode so I can see it, represented as a green outline. The video below demonstrates why this isn’t simple:

As you can see, running up a slope that then abruptly transitions to a table top results in the character catching some air. The same is true when coming off that table top. This is the correct, physically simulated behavior, but it’s not what I want to happen. I want the character to remain glued to the ground until they jump, or until they drop off an actual ledge. There are a bunch of other complications, some of them simple to solve (like the character sliding downhill) and some of them arbitrarily complex. This is yet another demonstration of why it’s worth considering *not* using a physics engine to create platformer games, which often want non-physical behavior. Doing a little bit of research into what I was getting myself into, I came across a relevant forum post, quoted below:

The problem is Box2D rewards consistency, and old school platforms are anything but. They are usually implemented as hacks (beyond the basics, I mean).

It’s my belief you can recreate most of the feel in Box2D, by discarding large and large amounts of the physics. E.g. start doing your own velocity calculations instead of using ApplyForce. But the more you discard, the worst the physics becomes.

For the record, I’ve been manually applying linear velocities since the very beginning of the project. Another indie game dev chimes in:

Echoing the sentiment that all collision is a hack. The only time it isn’t is when everything in the world can be defined in some extremely regular and consistent way. Approximating characters as AABBs, circles, capsules, or spheres and adjusting their boundaries to fit sprites/models as they perform different animations breaks all the “expected behavior” that makes a physics engine go. Along the way, you pretty much always end up with game-specific stuff where you say something like “oh I want the player to be able to grab ledges, and crouch-walk through low areas” and then you have to add hacks that cross collision with animation and player state to get the effect down. Hence the motto of my old boss, “collision is gameplay.”

This sounds… nauseatingly familiar.

So, what’s my problem? Why am I giving myself this headache?

First, I just played the new indie Metroidvania Operation Smash, which has a ton of lovely organic environments that use sloped surfaces all over the goddamned place. It’s pretty great. Seriously, you should buy it. Slopes definitely allow your environments to look a lot more interesting and varied.

Second, and this is the more important factor, I’ve wanted sloped terrain since before I started serious work on the game. Slopes allow you to sprint through an area and change your elevation without jumping. That’s a pretty great feature, which Super Metroid makes use of to great effect. The only reason I banned slopes from the game was to make it simpler to implement and therefore faster to get done, which is laughable at this point in the development.

I’m calling this an experiment, where I have a week to convince myself that either slopes are great or totally not worth the effort. I honestly think it could go either way.

Demoing the dash booster

I know, I know: a “run faster” item is probably the single most hackneyed power up in the history of Metroidvanias. What can I say? I just really like how a nice dash adds to the control mechanics of a platformer. And of course it can break a certain kind of block. See it in action below:

This is a true dash, activated with a double-tap of the run button. This is in contrast to Super Metroid’s similar powerup, which just automatically kicks on once you hit a certain ground speed. I think the latter is actually better for Metroidvania planning purposes (since it requires a long runway), but a true dash adds a lot more to game play.

I’m not sure about whether this item will level up, so that each level-up you find makes it faster — or if it can be activated multiple times to get faster and faster boost speeds (similar to the “long runway” lock and key semantics of Super Metroid’s dash booster). I have it implemented as the “long runway” option at the moment, but I’m still mulling this one over.

On an implementation note, this is the first time I have had a reason to use Box2D’s broad-phase collision event. In theory it’s for exactly this kind of thing: warning you about an upcoming collision event before it actually takes place. In my case it looks like this:

private void OnBroadphaseDashCollision(ref FixtureProxy a, ref FixtureProxy b) {
    if ( _isDashing
            && (a.Fixture.GetUserData().IsPlayer || b.Fixture.GetUserData().IsPlayer)
            && (a.Fixture.GetUserData().IsDestructibleRegion || b.Fixture.GetUserData().IsDestructibleRegion) ) {
        Fixture f = a.Fixture.GetUserData().IsDestructibleRegion ? a.Fixture : b.Fixture;
        if ( (f.GetUserData().Destruction.DestroyedBy(EnceladusGame.DashDestructionFlag)) ) {
            Tile tile = TileLevel.CurrentLevel.GetTile(f.Body.Position);
            if ( tile != null ) {
                TileLevel.CurrentLevel.DestroyTile(tile);
            }
        }
    }
}

But of course that’s not good enough. Once you get moving fast enough, you stop getting warned about the receding edge of the wall that’s being chewed up and run smack into it on the same frame it was broken apart. This isn’t really Box2D’s fault, just a natural effect of how I’ve implemented the terrain in the game. To patch that up, I add an actual collision handler that ignores collision events that will result in a dash-booster-vulnerable block breaking:

private bool DashCollisionHandler(Fixture a, Fixture b, Contact contact) {
    if ( _isDashing ) {
        if ( b.GetUserData().IsTerrain && contact.GetPlayerNormal(_body).Y == 0 ) {
            FixedArray2 points;
            Vector2 normal;
            contact.GetWorldManifold(out normal, out points);
            var tile = TileLevel.CurrentLevel.GetCollidedTile(points[0], normal);
            if ( tile != null && TileLevel.CurrentLevel.IsTileDestroyedBy(tile, EnceladusGame.DashDestructionFlag) ) {
                TileLevel.CurrentLevel.DestroyTile(tile);
                return false;
            }
        }
    }
    return true;
}

Not the most straightforward feature to cram into Box2D, but definitely a long way from the hardest.

So can I jump or not? Determining if the ground is under your feet in Box2D

I now have a near-total grasp why the developers of the Box2D physics engine “caution” its use in platformer games. It’s really a poor match for a whole host of common interaction patterns, the most basic one being one of the most difficult (and important) to get right: is the character standing on solid ground?

The basic tutorial to answer this question doesn’t work properly, since counting collisions and separations with the character’s feet isn’t accurate in all cases, such as when bodies are created or destroyed the same simulation step as they touch the character’s body. When the count gets off, you’ll either have a character who can’t jump (broken game) or a character who can air-jump endlessly (sequence-broken game). The only thing to do is to verify, by examining the contacts touching the character’s body, whether there’s solid ground under their feet or not.

Except this doesn’t work properly in all cases, either, if you ever resize the polygon of the character’s body. Does your character need to duck, or to crawl? Then you’ll either need to a) resize or recreate his body when this happens, or b) use prism joints and multiple fixtures to achieve the same effect. The second option sounded like a rabbit hole to me, but I was still running into cases where I was getting non-physical behavior after a resize event, which you can see happening at around the 45-second mark below.

I’ve been coding this game, off and on, for over a year now, and I just this week finally figured out how to subdue this buggy behavior. I’m not going to kid myself that my fix won’t cause other bugs further down the line, but it at least addresses all the ones I know about. It consists of two changes:

  1. Don’t rely on Box2D to tell you about collision and separation events to determine whether the character is standing on solid ground. Instead, check manually every frame, using contact with the ground as a primary source and ray casts to make sure. This is the only reliable method I have found to answer the “can I jump” question when bodies are being created, destroyed, and resized.
  2. When resizing a body (e.g. to duck), Box2D can exhibit non-physical behavior, like bouncing it away from a surface it was already in contact with. To resolve this problem, ignore any updates from existing contact edges for one frame after the resize.

The latter change looks like this:

/*
 * Resizing the body sometimes results in our slight 
 * overlap with the floor, resulting in Box2D detecting 
 * a collision and bouncing the body upwards. To get 
 * around this, we just disable any existing contact 
 * edges with terrain for the next timestep.
 */
ContactEdge edge = _body.ContactList;
while ( edge != null ) {
    if ( edge.Contact.FixtureA.GetUserData().IsTerrain 
        || edge.Contact.FixtureB.GetUserData().IsTerrain ) {
        edge.Contact.Enabled = false;
    }
    edge = edge.Next;
}

Hopefully someone find these tips useful. For what it’s worth, if I were starting the project again today, I wouldn’t use a physics engine — my game doesn’t need realistic physics. What it does need is a collision engine, which Box2D does a decent job at. Unfortunately, it’s really hard to just take that one piece without grabbing more, and even that one piece is unreliable when bodies are frequently created, destroyed, or resized. Since these things happen pretty often in many platformers, I can’t really recommend Box2D (or maybe just Farseer) for this use case.

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.