First original map artwork

“Artwork” might be a bit of a generous term in this case, but here it is. I’m working on the first map of the game, on the ship going to Enceladus, drawing the tiles necessary to convincingly depict an interplanetary vessel. Here’s the outline of the ship as it appears zoomed way out in the map editor:

Not very ship shaped at the moment

And here’s what it looks like in the game:

I’m using Pyxel Edit to draw the tiles, then laying them down with Tiled. It’s a reasonable workflow for the most part, except that I have to make sure to never, ever change the order of the tiles in the exported tile sheet or else face starting over entirely. Also, I’ve found some annoying, but not deal-breaking, issues with Tiled’s auto-mapping feature, which I’m using to automatically fill in the collision layer with whatever is in the foreground layer. The system is convoluted and confusing, there’s no concept of a wildcard, and I just learned that each orientation of a tile counts as a different tile in the rule set. Even for the very limited tile set so far, I have a huge auto mapping rule.

This is already getting out of hand.

This map also exposed some buggy behavior in the camera controls during room transitions (very obvious in the video) that I’ll have to sort out. I’m a bit torn how to fix this issue in the general case. The problem is that the character is moving between two rooms with different height ceilings, so his vertical position on the screen will have to change. It would be easy enough to make that a smooth transition, but I’m left asking myself a question that I ask myself embarrassingly often: What Would Super Metroid Do? I have a gut feeling that Super Metroid would design the level map so that the ceiling height of two adjacent rooms is the same. I’ll have to sleep on it.

The problem with pasting a static torso onto wildly pumping legs

… is that it pretty much looks like that’s what you did. Nevertheless, the moving-while-aiming animations are all produced with this technique. Specifically, I take the torso from the “stand still and aim” animation, then put it onto the legs of the moving animation. This doesn’t lead to initially fantastic results — it looks pretty fake, as a matter of fact. From there it’s a matter of pixel twiddling and subtle effects to get it to look passably natural. One big realism booster is making sure the torso bobs up and down with the cadence of the run. It’s only 4 or 5 pixels of displacement, but little things like that make all the different in the world.

I got the last of the moving-while-aiming animations, eight-way aiming while running, done today. Have a look.

I’m a bit torn on what to work on next. It’s either going to be 1) damage animations, 2) health screen overlays, or 3) a device I’ve been calling the scooter. Time will tell!

More sprite work

Despite what I said yesterday about it being a “nice to have”, I decided that 1) it was actually pretty important to allow aiming independent of movement on the ground, and 2) all things being equal, I should probably get a sense of how that looks and plays pretty early on. I think my attitude towards it yesterday had mostly to do with the thought of there being so many sprites to animate to achieve this effect, but the more I thought about it the more I thought it was essential to basic gameplay. So I bit the bullet and cranked out lots and lots of sprites today.

Above you can see aiming in all directions when walking and jogging. Only running remains, and then the character will be pretty much fully animated for his basic moves. Since I’ve more or less decided that every power-up / tool will fire from the barrel of the gun, “basic moves” covers a lot of territory.

I’m a bit worried about the amount of sprites in memory, although so far XNA has given no indication it’s a problem. If I start bumping into some constraint, I can switch to sprite sheets, from individual sprite files, pretty easily thanks to Graphics Gale, which I’ve been using for all my graphics editing work.

Aim in all the directions

Some more sprite work and control revisions today, resulting in eight-way aiming when standing, ducking, or in the air. You still can only aim straight forward when moving on the ground; I’m not sure if I’ll change that or not at this stage. I’m considering it a “nice to have” for the time being. Here’s what all the wonderful sprites look like:

Although you can’t really tell from the video footage, one major change in this version is that aiming can be accomplished with the right control stick. This is especially important to enable ducking and aiming at the same time. But since it’s awkward to use the right stick and a face-plate button (A,B,X,Y) at the same time, the left stick can still be used to aim when appropriate — this makes it much easier to aim straight up or down when jumping, which will be relatively common. Also in this build, firing weapons is remapped to the right trigger and right bumper. Long term, the two bumpers and the Y button will probably be assignable quick-equip buttons, and the right trigger will be how you fire the currently equipped weapon.

Tomorrow I’ll finally be fine-tuning the controls in preparation for experimenting with new weapons and game mechanics.

Implementing a pass-through 3.0 vertex shader for games without vertices

I stated earlier that it was impossible to use a vertex shader in XNA when writing an entirely 2D game. Like a lot of things I fervently believe, this turned out to not actually be the case. When fancying up my original wave-distortion shader to make it work in arbitrary directions, I had to do quite a bit more trigonometry, and this pushed me over the maximum of 64 arithmetic slots allowed by shader version 2.0. I had a choice: I could either get smarter about my math, or figure out how to make shader version 3.0 work with XNA sprite batches. It actually didn’t turn out to be that hard once I believed it must be possible.

The problem I was seeing when attempting to implement a vertex shader naively before is that the entire screen would render one solid color during the duration of the Effect. The key seems to be that you need to create an orthographic projection the size of the screen and then multiply it by a half-pixel offset translation matrix, like so:

Matrix projection = Matrix.CreateOrthographicOffCenter(0, spriteBatch.GraphicsDevice.Viewport.Width, spriteBatch.GraphicsDevice.Viewport.Height, 0, 0, 1);
Matrix halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);
effect.Parameters["MatrixTransform"].SetValue(halfPixelOffset * projection);

For the record, omitting the half-pixel translation will make your full screen post-processing effect a bit blurry. It’s been too long since I took a graphics course to remember precisely why that is, and I’m too lazy to learn it until I absolutely need to.

Anyway, once you have the above in your XNA code, then your version 3.0 vertex shader is as simple as:

float4x4 MatrixTransform; 

void SpriteVertexShader(inout float4 vColor : COLOR0, inout float2 texCoord : TEXCOORD0, inout float4 position : POSITION0) { 
    position = mul(position, MatrixTransform); 
}

The underlying reason that you need to specify a vertex shader for a version 3.0 pixel shader but not a version 2.0 pixel shader seems to be that, when you don’t specify a vertex shader explitly, XNA will give you the default one supplied by the sprite batch, and it uses some ridiculously old version like 1.1, which is incompatible with 3.0 (but not 2.0). I’m the first to admit I find this whole shader business confusing and often counter-intuitive, but surely that will wane with experience.

Another day, another sprite

Got the directional aiming sprites whipped up today, and also made it possible for multiple weapon-effect post-processing shaders to be on the screen at once:

The standing animations were incredibly fiddly and involved lots of manual tweaking, relative to other animations. I have additional transition frames for each aiming direction, but haven’t gotten to putting them in yet.

Tomorrow is the fourth of July, so I may be busy blowing physical things up, not just the integrity of software like usual.

Animation continues

I got three gun-holding animations converted to sprites and integrated today:

I also played around with support for parallax-shifted backdrops. The pine needles are a little big for the scale of this world — it’s just a test to see how it looks.

What remains to be done on the animation front:

  • Tweak the speed triggers — right now it’s possible to jog at a ludicrously slow pace.
  • Map suitable transitions from each animation to each other, so that it always appear smooth when going from one to the other
  • Change jumping animation to hold a gun
  • Sprite work for gun aiming
  • Merge gun aiming into other animations as necessary

I’m not sure if I’ll support aiming diagonally while running or walking — it might be more trouble than it’s worth. It seems like forcing the player to stop moving in order to aim up or down isn’t the end of the world. We’ll see how much work the alternative is.

I’m pretty happy with how the animations have turned out so far. An artistic style to match them is slowly crystallizing in my brain.

Animation is hard

So I finally got the live-action footage I shot on Thursday converted into sprites and integrated into my game. Here’s what it looks like at the moment.

What’s not done yet:

  • Holding the gun in five different alignments
  • Top-speed run animation. Right now it’s a jog, but I want to change the animation when the speed is high enough.
  • Ducking

I learned some things from this process.

First, long wind-up animations for things like jumping do increase realism, but at the cost of responsiveness. I played around with varying amounts of delay between initiating a jump and leaving the ground, during which time the wind-up animation would play: the character crouches, swings his arms, and launches himself in the air. But I found that a delay of any longer than 50 milliseconds really impacts the controls, and that only gives you time for three frames of wind-up. I chose three key frames, but they play too fast and the effect is kind of ruined. I’ll probably end up taking them out altogether.

Second, animation is hard, and the work scales with the number of frames you have. I’m relatively tied to my high frame count (I really like how smooth things look), which means I am contemplating pushing things in the other direction, to make the overall graphical feel more simplified and abstract. It worked for Geometry Wars.

Third, it’s pretty tough to film an orthographic projection in someone’s back yard. I’ll need to go back in and do some cleanup to make sure all the sprites look properly two-dimensional and are the same height.

Finally, being able to tweak animation parameters at run time is essential in getting the animations looking reasonable without spending an entire week on it. I discovered the proper ratio of speed / animation cycling when walking or running by using this technique.

Next I’ll be working on getting the gun integrated, and working on eight-way shooting complete with animations. Once I have a rough idea how those look and feel I’ll be able to move on to experimenting with other weaponry and tools.

Implementing a wave-distortion shader with Nvidia FX Composer

I spent most of the day fighting with one of most difficult and arcane pieces of software I’ve ever used: Nvidia FX Composer. I think it was written five or so years ago by members of a suicide cult, who upon releasing it promptly all drank the poison cool aid. Nvidia makes a pretense of running a customer support forum for the product, but most of the posts are desperate questions without a single response. On the other hand, the included documentation is relatively massive… but years out of date and therefore occasionally completely inaccurate, which in many ways is worse than not having any at all. Of course, my own total lack of experience writing HLSL didn’t help, either.

In the end I persevered and got this effect implemented and merged into my game:

Once I finally figured out how to use it, FX Composer was a huge help in being able to rapidly experiment with different effect parameters and see the result. Here’s what the software looks like once it stops kicking you in the balls:

What I had so much trouble with was figuring out how to convince the software that my shader was a post-processing effect to be rendered onto the entire scene, not onto a 3D object in the scene. After a lot of research and experimentation, I started with a working post-processing shader provided as a sample and starting taking things out of it until it broke. That’s when I learned you need these special semantics to tell FX Composer that the shader is for post-processing:

float Script : STANDARDSGLOBAL <
    string UIWidget = "none";
    string ScriptClass = "scene";
    string ScriptOrder = "postprocess";
    string ScriptOutput = "color";
    string Script = "Technique=Main;";
> = 0.8;

If this looks like gibberish to you, it does to me too. But these are the magic words needed to get FX Composer to let you apply a shader to an entire scene. It’s also important that you have a vertex shader defined in your technique, even if you’re only using a pixel shader — nothing will work otherwise. Here’s a pass-through shader you can use:

struct VertexShaderInput
{
    float4 Position : POSITION0;
	float2 Txr1: TEXCOORD0;
};
 
struct VertexShaderOutput
{
    float4 Position : POSITION0;
	float2 Txr1: TEXCOORD0;
};


VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;
  
    output.Position = input.Position;
	output.Txr1 = input.Txr1;
    return output;
}

The final piece of the puzzle was getting the updated shader back working in XNA. There are lots of articles on this on Google, so no need for me to repeat it here. But I did discover something interesting: if you have an entirely 2D game, not a single 3D triangle rendered anywhere, then using a vertex shader in your effect, even the pass-through shader, will cause no pixels to get through to your pixel shader. The solution is to just comment out the vertex shader in your pass. Since FX Composer requires this shader and XNA requires it be absent, toggling the comment is an easy way to make both systems happy.

technique Main <
	string Script =
	"RenderColorTarget0=ScnMap;"
		"ClearSetColor=ClearColor;"
    	"ClearSetDepth=ClearDepth;"
		"Clear=Color;"
		"Clear=Depth;"
	    "ScriptExternal=color;"
	"Pass=Pass0;";
> {
    pass Pass0 <
       	string Script= "RenderColorTarget0=;"
			"Draw=Buffer;";
    >
    {
    //VertexShader = compile vs_2_0 VertexShaderFunction();
	PixelShader = compile ps_2_0 PS_wave();
    }

This was a bit more of a rabbit hole than I really intended to climb into, but at least I learned a few somethings.

Also, I filmed the motion-capture sequences for the main character’s animations today in Roark’s back yard. I need to do a lot of video editing on the result, then the hard labor of cleaning up all the pixels and shoving it into the game, but that’s tomorrow. For now, enjoy this outtake from the shoot:

Live footage test of motion capture

I told Roark to come over wearing all black, or at the very least dark clothing. He showed up in faded jeans. But that was good enough to provide proof of concept for my de-rezzing motion capture program. Here he is going through a couple test motions:

You can tell that the jeans just aren’t dark enough to provide a good contrast against the backdrop — if I crank the brightness threshold up far enough for them to get colored in, we start to pick up other elements of the scene as well. For actual game animation footage, we determined he needs to be wearing something like a black unitard (ideally) on a nice bright white background, preferably on an overcast day to avoid any sharp shadows. That will give me the pixelated humanoid character, which I can then paint like a coloring book into whatever outfits I want, adding in things like guns and helmets in post production. We’re scheduled to shoot next Thursday, so with any luck I should have the basic running, jumping, and ducking animations to show off some time in early July.