You don’t understand how complex your program is until you break it

Today, I had a simple goal. To write the prologue to the game, the door to the ship needs to be locked initially. Then, after you explore it and experience some quality story telling, the ship lands, and the door can be opened to head down to the surface of the moon. So I needed to be able to name doors on my map editor, then refer to those named doors programatically when I script events, and tell a particular door to lock or unlock itself. Sounds simple, right? Sounded pretty simple to me, so I threw in some refactoring to make the whole system more efficient along the way. And then this happened.


That should be a door, not a gaping white void.

This is one of my favorite kinds of bugs to discover: you change something seemingly harmless, and a seemingly completely unrelated component of the program wigs out. That’s certainly what happened here. So what caused it?

As you can see in the diff, the main change to this subsystem was to transfer ownership of all door objects to the overarching “Game” object so it could manage their lifecycle along with all the enemies, projectiles, and other dynamic elements. Somehow, this made them paint white. To understand why, you have to understand how I chose to implement a shader to turn a sprite a solid color. To draw a sprite in XNA, the incantation goes:

spriteBatch.Draw(Image, displayPos, rect, Color.White);

That last parameter normally only controls the alpha channel… unless you’ve written a custom shader that uses the color parameter information. When I transferred ownership of the door objects, they started getting drawn by a different SpriteBatch round, one that had this custom shader applied to it. To disable the solid-color effect, you just need to pass the magic color Black. I bet there’s probably a better way to do this.

sampler TextureSampler : register(s0);

float4 main(float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR
    // Pure black is the cue to disable the solid-color effect
    if (color.r == 0 && color.g == 0 && color.b == 0) {
       return tex2D(TextureSampler, texCoord) * color.a;
    } else {
       return color * tex2D(TextureSampler, texCoord).a;

technique SolidColor
    pass Pass1
        PixelShader = compile ps_2_0 main();

Anyway, I did eventually get everything working the way I wanted, so that now the opening scene triggers the ship’s door to lock. Here’s a pretty unimpressive video demonstrating a door not opening when it gets shot.

Now I need to do the comparatively lighter work of writing all the dialog and event scripting to make this all hang together just right. I also need to pare down the currently over-stuffed cast of characters to just five to simplify matters.

Leave a Reply

Your email address will not be published. Required fields are marked *