Flash 9 offers the beginGradientFill() method for drawing N-color 1D linear and radial gradients. However, I couldn't find a way to draw 3-color 2D gradients as seen in OpenGL.

I could compute the gradient myself and just set pixel values in a BitmapData, and then render those pixels to make my triangle. But that's no fun. It turns out BitmapData objects support lots of interesting operations like add, multiply, subtract, and even a limited form of lookup.

By incredible coincidence, triangles have three vertices and colors have three components. So you can use colors to store information about vertices. (Note that in the 3D DirectX/OpenGL shader world we do something similar by storing normal maps in color channels.) Here's what I came up with:

Make a triangle bitmap where the red, green, and blue components of the color correspond to the barycentric coordinates of that point in the triangle, scaled to 0–255 instead of 0–1. I only have to do this once.

Make a gradient array for the three colors I want at the vertices. The gradient array is 256 elements and it's merely the red, green, and blue values scaled down. At position i I'll store the color scaled to i/255 brightness.

Use BitmapData.paletteMap() to map the red, green, and blue components to the scaled colors in my three gradient arrays. The paletteMap() function adds the color values in those arrays to produce the final color.

This is what the triangle bitmap looks like. The red, green, and blue channels represent the barycentric coordinates:

The way this technique works is that paletteMap() calculates the output color at each pixel to be array1[red] + array2[green] + array3[blue], where the indices are the color components from the input array. In the input array, red+blue+green equals 255, and the three arrays have the color scaled up to 255. So this gives me interpolation between any three colors. For example, if red is 20, green is 80, and blue is 155, then the final color is array1[20] + array2[80] + array3[155] = (20/255*color1) + (80/255*color2) + (155/255*color3), which is the interpolation we want. Here's an example of what happens when we apply this to a triangle with green, purple, and yellow vertices:

I should also note that paletteMap() can take four arrays instead of three; the fourth is alpha. I tried building an Alpha+RGB quad bitmap for mapping, and then using four colors with that. Unfortunately I couldn't get it to work. I think paletteMap() uses alpha both for the map and for the blending (this is undocumented). Or maybe it's doing something else I don't understand. In any case I gave up on it.

The big downside of this technique is that you have to fill those arrays each time. In general, this could get expensive. If the triangles are small, it'd be faster to directly compute the interpolation per pixel (the "not fun" technique I mentioned at the top). However, in some situations you only need a few colors, and can precompute those gradient arrays. A friend of mine wants to use this for lighting calculations, and for white lights you can precompute all 255 light levels. I was hoping to use gradients to render a map (like the one in the spaceship editor demo), and if my maps contain only a few terrain types (grass, sand, etc.), I can precompute all my gradient arrays. I can use this technique to compute a color gradient bitmap, and then mix with those with my terrain textures using the standard blend operations.

Eventually I'll be using Flash 10, and that'll open up new techniques, especially with shaders. But for now, I'm using Flash 9 for my site, and I'll try to push BitmapData to do some things it probably wasn't meant to do.

Last year I decided that I just don't have the time and energy to work
on a full game, and instead I should work on quick prototypes
instead. I had planned to try an idea every few weeks. The space
station
generator
was one of these; the spaceship
editor
was another. Spaceship physics turned out to be really interesting
and I kept working on it. It's been months now. I have lots of other
things I want to try, so I need to wrap this up, even if there's more I could do.

In the last
post
I talked about the physics. The final step was to figure out whether
there were interesting tradeoffs that a player could make when
designing a ship. I needed a spaceship editor.

I made a circle that could be dragged around with Flash's built-in
draggable object feature, but I switched to handling the mouse events
myself, so that I could add "snap to angle" and other constraints. It
wasn't long after having a draggable circle that I made a circle for
each thruster. And then two, one for the head and one for the tail. I
also wanted the thruster to move as a whole if you dragged the base,
but only the tip to move if you dragged the end. I overlaid these on a
large version of the ship to make a ship editor. Now I could start
answering the key question: are there interesting design choices?

After playing around with lots of configurations my answer was
no. It's always better to put the forward thrusters
near the wingtips. You don't lose any forward thrust, and you gain a
great deal of rotational speed. The other thrusters were less of an
issue, because they were used for less common motions.

To make this interesting I need some disadvantage of putting forward
thrusters at the tips. One thing I considered was that thrusters at
the edges of your ship are more vulnerable to damage. However, I don't
actually have a game here, so that's not something I can easily
test. So I turned to the other idea: thrusters require support that
have mass. Putting a thruster far from the center requires more
massive supports, which slow you down. I started with mass and moment
of inertia calculations from physics, and then used tuning and
experimentation to come up with something that felt reasonable. The
main tradeoff I focused on is between rotational and forward
acceleration, when both are from the same forward thrusters. If you
move the thrusters near the wingtips, you get lots of rotation, but
you need a lot of mass for the structural supports, and that slows
your forward acceleration. This plot shows acceleration versus
thruster distance from the center:

It shows that at least for the forward thrusters, there's a nice
tradeoff between rotational and forward acceleration, and you can't
get unlimited rotational power because the supports become too
massive. There are lots of other things I could look at, but I think
some tradeoffs depend on the game (for example, they may be completely
different if the ship is in an atmosphere than if it were in space),
and I'm not writing a game right now.

I eventually recognized that I could keep working on this for a long
time, but I need to move on and explore other topics. I've mostly
answered the question I set out to answer: if you let the player
design the spaceship, are there interesting tradeoffs arising from the
physics? I think the answer is yes, but probably not as many
interesting tradeoffs as Spore's approach would allow. In addition,
things weren't automatically interesting; I had to design a tradeoff
and then tune the physics to make that tradeoff show up, and I had to
be careful to avoid overpowered ships. That was unsatisfying. In a
real game I imagine I'd have to manually design lots of tradeoffs for
different game levels or areas of the world, and it'd be quite tough
to test all the combinations. And even if I could do that, controlling
sliders in a continuous tradeoff space seems less fun than picking
from some really interesting manually designed items in Spore or
Ur-Quan
Masters. So
yes, I was able to make what I set out to make, and it was fun to play
with, but probably would be a lot less work and more fun to give
people a few interesting choices.

The Demo

Of course after all that I should let you try designing your own ships
and see what you think. Instructions:

Press T to change ships (you may have to click on the
map first to get focus). The demo ships are: yellow square (4
thrusters), green square (like yellow but you can make it
asymmetric), teal curved, purple curved (like teal but two
thrusters are broken, so it can't fly properly), blue square (8
thrusters!), red triangle (goes fast but can't turn well). I
mostly play with the yellow and blue ships.

Change the ship by dragging the thrusters and adjusting their size
and orientation. As you do this, watch the graphs. The bar charts
show what happens if you press one key at a time. The polygons
represent your current flight envelope for two keys at a time. I
find that I use the W+A and
W+D combinations often, so I watch the chart
in the lower left. The shaded ovals show the "target" that you
want to mostly cover if you want a reasonable ship. The yellow and
blue ships are easy to improve: just increase the power of the
thrusters and they will fly well. The teal and red ships are
slightly harder, and the purple ship is near hopeless. In a real
game the number and power of your thrusters would be limited, and
fuel efficiency and fuel tank size would be additional factors to consider.

The demo is written with Flash 10 in mind. It should work with Flash 9
but there may be some visual artifacts (due to bugs in Flash 9 that I
didn't work around). There are some other minor bugs that I might fix
someday.

I'm finished with the spaceship editor mini-project and haven't
decided what the next mini-project will be. I really enjoyed working
on the spaceship physics and editor, but I spent too much time on it,
and hope to spend less time on the next mini-project.

Update: [2009-12-05] If you want to try a game that lets you create your own spaceships, check out Captain Forever. It's pretty neat.

Update: [2012-08-16] Another game that lets you design your own spaceships is Gimbal.