Collecting a powerup

Checking off my list of vital features before an alpha test release, today I implemented a system for keeping track of which collectible items (upgrades and the like) are still available to collect. And I implemented collecting one of them, the propulsion wheel:

I’m not quite happy with the collection announcement yet — I think it needs a little more tweaking before I’m done with it.

For you aspiring game developers reading this and wondering how I got the 2-d sprite to appear to rotate about the y-axis, it’s pretty simple. Like most tricks of this nature, it just involves a little trigonometry:

bool reversed = _rotationTimer > RotationTimeMs / 4 && _rotationTimer < 3 * RotationTimeMs / 4;
Vector2 scale = new Vector2((float) Math.Abs(Math.Cos(_rotationTimer / RotationTimeMs * Math.PI * 2)), 1f);
spriteBatch.Draw(texture, displayPosition, null, Player.Instance.Color, 0f, origin, scale,
                 reversed ? SpriteEffects.FlipHorizontally : SpriteEffects.None, 0);

Basically I'm periodically shrinking the image down in the X dimension, then bringing it back to full size, while also flipping the image horizontally for half of the rotation. The trig functions make it look like it's actually smoothly rotating because the unit circle yo.

Tomorrow I've decided to tackle drawing the sprite sheets for the surface of the planet. I also think I might need to shift the entire Tiled map over and down some... all the layers of it. Hopefully that's not such a horrible task as to require me to write a program to do so.

A rainbow colored title screen

I whipped up a title screen, the first thing the player sees when playing the game, and then made it a little over the top with a handy guide I found called making annoying rainbows in javascript. I think it turned out really nice!

One thing I’m not certain about is how I’ll end up representing a save slot on the title screen. As you can see, I’m using the date of the last save at the moment, but that looks a bit yucky. Ideally I’ll eventually settle on a graphical representation of game progress along with something like elapsed game time, but that’s work for another day.

Tomorrow I’m either going to work on adding music, make player power ups collectible (right now you start with all of them), or else do some sprite work for the first level. Maybe all three!

Loading saved games

The code to load a saved game is now ready to roll, as the following slightly boring video demonstrates:

As you can see, the map visitation data, event status, and location get properly reset when a saved game is loaded. I’ll eventually need to persist health and powerups as well, except the systems managing those things are just stubs themselves right now.

I also switched to Json.NET for save file persistence after figuring out that XMLSerializer doesn’t handle interface types at all. That makes it worse than useless, in my humble opinion.

Tomorrow I’m going to attempt to make it all the way through a title screen, where you can start a new game or load an existing one. Don’t count on it being very pretty.

Time for a pause menu

My first piece of interactive GUI! It’s just text, but it gets the job done. Have a look!

I haven’t implemented loading a save state yet — I needed a way to invoke it from within the game. Getting save files loaded is first on the docket tomorrow.

I also sliced the tip of my thumb off making lunch. It’s really cramping my style. I guess I’m lucky it’s my Alt-Tab thumb and not my space bar thumb.

Drive-by saving

I implemented actual saving, along with a bunch of other bug fixes and code cleanups that have been bugging me for months. It turns out that saving is so fast it’s unnoticeable, so I built in some fake delay to make it seem like something is actually happening. This is what it looks like when it happens:

You will never believe how Microsoft officially recommends you save game data. Well, maybe you would.

string filename = "savegame.sav";

// Check to see whether the save exists.
if (container.FileExists(filename))
   // Delete it so that we can create one fresh.
   container.DeleteFile(filename);

// Create the file.
Stream stream = container.CreateFile(filename);

// Convert the object to XML data and put it in the stream.
XmlSerializer serializer = new XmlSerializer(typeof(SaveGameData));
serializer.Serialize(stream, data);

// Close the file.
stream.Close();

That’s right: the official developer documentation for XNA tells indie game developers they should delete the existing save file before writing the new one to disk. Normally my issue with Microsoft’s documentation is the byzantine twists required to track down the simplest piece of useful information. But I don’t know if I’ve ever before seen them give advice that wasn’t just bad, but dangerously wrong.

Next up is the screen management system (menu screens, etc) that was too big to start until I did some cleanup, leading to me finally renaming the game assets from “Arena” to “Enceladus.” I started the game project under the name “Arena” with the idea that it was just a test bed that I would throw away when I wrote the real game project. But, as always happens, the prototype slowly accreted into the actual project. I hadn’t bothered to change the name because I wasn’t sure exactly how to do it in Visual Studio, and also because I wasn’t completely sold on the name “Escape from Enceladus.” I happen to like the name a lot, but it’s a bit odd, and it also has the problem of being impossible to spell or pronounce for most people, which will make word of mouth marketing pretty tough. Today I finally decided I didn’t care and pulled the trigger. It’s kind of incredible how much more real that makes the game feel.

The illusion of saved games

I started reading up on the confusing mess that is persisting game data with XNA, then decided it would be more fun to implement placing the save stations in the game world. They don’t do anything yet, but I can place them wherever on the map. I’m not sure yet what other kinds of things, besides beds, will let you save the game.

Tomorrow we’ll see if I can’t crack into actually saving game state, along with some simplistic GUI for saving and loading games.

Dialog rampage

Today I went on a serious tear, dialog-wise, and wrote all the dialog for the prologue of the game. The resulting video footage of the first “cutscene” of the game makes for truly terrible watching — I’m advancing the dialog too fast for you to actually read, but still slow enough to take forever. The video does give you an idea of how the cutscene plays out, though, with colors standing in for the characters’ names.

If you want to read the actual dialog you can find the source here and here.

There’s quite a bit of dialog, actually. This is something that vaguely concerns me, the idea that I’m turning off some players who will be all like “WTF, reading?! I’m gonna go outside and better myself.” I’ve thought about this, and I’ve determined that I’m basically OK with that. What’s the point of a money-sucking passion project if you can’t act like a complete prima donna and alienate a ton of your potential audience?

In all seriousness, this is probably over a third of the dialog for the entire game as I have it planned now. For some reason, the characters on the ship loom large in my mind, and it seemed important to establish who they were so you know who you’re saving from certain space madness. You will barely talk with them for the rest of the game, unless you head back to the ship for some reason. But they will be there to make half-witty conversation if you do!

Next up, I need to finally tackle the save system. This ought to be interesting.

That’s more like it

I decided that before I went haring off over the entirety of the ship a hundred times testing the introductory story sequences, I should really make the control feel a little better. It had been bugging me for a while. A couple aspects in particular just made it feel sluggish and unresponsive:

  • It took too long to get moving when you were on the ground
  • Jumps felt downright floaty
  • Changing direction in the air was basically impossible

Addressing just these few issues made a radical difference. I’m kind of blown away what an improvement I was able to achieve with minor tweaks in behavior. Here’s what it looks like in another emotional rollercoaster of a video:

For the movement on the ground, I was starting the player off relatively slowly, then uniformly accelerating them if they had the run button held down. The initial speed values for walking and running were just too slow, and bumping them by 40% or so immediately made the control feel more responsive. Before, trying to build up any speed was like running on a slick surface, but stopping was always immediate. Now, whether you are walking or running, you start at a fairly good clip, and even though you’re still accelerating relatively slowly (2.5 seconds to accelerate to full running speed) it feels like you’re going places immediately.

Getting the jumping to feel satisfying was a bit harder. A lot of game bloggers recommend not using a physics engine for platformers because it’s too hard to make the jumping to feel right. But those who have pulled it off have one piece of advice: pump up the gravity by a lot. I increased my world gravity to 30 m/s/s, then played around with the jump take-off speed and how long holding down the jump button would make the jump boost higher. Fiddling with those numbers at runtime using the control tweaking system I built earlier, I was able to get the jump height where I wanted and feeling much snappier. Finally, I decided to enable much more meaningful air control for jumps. It’s completely unrealistic to be able to control the arc of a jump in the air, but for this kind of game it makes things much more enjoyable.

If you’re interested in digging around in the player control code, you can find the relevant changes here and here.

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.

WhiteDoor

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.

Making story-writing possible

Today I hit a decent sized milestone in getting the first draft of the engine’s “event” and “game state” sub systems working well enough to write most of the prologue (on the way to Enceladus). Here’s what the first moments of gameplay will look like, roughly. To summarize, the captain calls you onto the bridge, and when you get there and talk to him he tells you what’s going on. Then, when you talk to him again, he says something different.

Tada, story telling! With these basic building blocks I can *almost* write the entire first scene of the game — pretty sure I just need to make it so doors can lock and unlock and then write all the dialog and state-logic, and I’m there.

In designing this sort of thing, one always runs the risk of going so far up your own butt that you need climbing gear to get back out. Seriously, this is like CS porn: I’m creating a framework that will track and then execute arbitrary events to transmute game state. And when I talk about game state, I don’t mean keeping track of where the characters and bullets are; I mean the meta-state, such as “have I talked to that guy yet?” and “should this door be locked when the room is loaded?”

I forced myself to take a step back and follow the wisdom of writing code like I just learned to program. I’m here to put pixels on the screen, not write frameworks! So I settled for a system that enumerates all the serious game-state events and features lots of hard-coded logic referring to these states. It’s the simplest thing that could possibly work. You can check out the code on GitHub here and here, then get to work stealing my hard work to make your own blockbuster indie game!