1. Floating Point Representation in Space Games

    I love space games. I’ve been writing them for 16 years (and not releasing them for 15 of those). Here are some things you may not have considered about space games (especially of the 3D variety).

    This article deals with player positions. We’re assuming a multiplayer game, or a game with NPCs that have to move precisely. We’ll deal with what is precise enough as we go.

    Space is big

    I know, right?

    Most of the things you want to see and fly to in space are huge and separated by huge distances.

    Space is small, too

    Not everything in space is big. Enemies and friendlies and cargo and things like that are much smaller. You might not want to model individual dust grains, but it’s reasonable to think that you’ll have things that are very much smaller than a planet, like the player.

    To handle the smaller things, the most convenient unit of distance is a meter. It is usually convenient for artists, too. If you make it something larger, your models lose precision, which can lead to very bad results like intersecting geometry not looking right.

    Change in space is gradual

    Planets orbit in smooth ellipses. You can fire a thruster and move very slowly. Asteroids and cargo can be nudged out of position. All of these things are small changes in a sea of big numbers.

    That means…

    The simplest coordinate system doesn’t work

    If you’re using Flash or JavaScript, your basic number representation is double precision, so most of this section doesn’t apply to you. But you end up having to deal with the same issues at larger distances, so it’s good to understand the more obvious version of what you’ll have be facing later.

    The usual way of representing object coordinates is a vertex of three floats — x, y, and z. Floating point numbers have 32 bits to represent the number you put into them. For normal numbers with certain fractions, they are often rounded a little, but usually not in a noticeable way.

    The larger the number gets, the less precise a floating point’s fractional part is. The number automatically adjusts to have more binary digits — bits — for the whole number part. It’s done with exponents; you can imagine a floating decimal point.

    The practical result of this is that a coordinate system that would work well somewhere near (0,0,0) starts to break down when you are a long way away. How does it break down?

    Since it is using more bits for the whole number part, the fractional part is rounded off, to powers of 1/2. When the fractional part is down to 4 bits, the smallest change that can be represented is (1/2)^4 = 1/16. That’s already a big problem.

    1/16 = 0.0625, or a little more than 6 cm if each unit is 1 meter. Assuming the best case for operations and rounding, that means that if your object doesn’t move 3.125 cm (to the next rounding point) between times that your engine looks at it, then it doesn’t move at all. And your engine will probably process objects 60 times per second, or more.

    So, with 4 bits of fractional precision, the slowest any object can be moving is 60 * 6.25 cm = 360 cm/s, or about 13 km/h. They can have lower velocities, but when you add the speed to their position, nothing happens. The difference is rounded out, and they don’t move.

    Imagine trying to drink a soft drink with 6 cm/s precision. Either your hand doesn’t move, or you’re throwing the can at your face.

    Is this something you have to worry about in your game? I think it is. 

    Earth - Sun distance, km: 150 000 000
    Earth - Sun distance, m: 150 000 000 000
    As a binary integer: 10 1110 1100 1011 0010 0101 1100 0000 0000
    Length of Earth - Sun distance integer: 34 bits 

    34 bits is bad. The regular floating point representation is 32 bits, and of that, 1 bit is the sign and 7 bits is the exponent (which indicates where the decimal point is). You have 24 bits of precision and 34 bits of number, so 10 bits of distance information get rounded off.

    That means rounding to the nearest 512 m. Any difference less than a half kilometer in distance is zero. And, at 60 engine ticks per second, minimum velocity is almost 31 km/s. Escape velocity from Earth is about half that.

    This is terribly wrong.

    What about doubles?

    JavaScript and ActionScript don’t have what other languages usually call floats; their only number format is double-precision. The vast majority of languages give you the option to use something more precise than a float.

    Double precision helps in this case. It means that instead of 1+7+24 bits to express positions and speeds, you have 1+11+52 bits. 52 bits of precision is not almost twice as good; it’s two to the power of eighteen times as good. Awesome!

    So, at Earth’s distance, where we know the whole number part is 34 bits, 52-34 leaves 18 fractional bits of precision. Each meter is divided into 262 144 parts. That’s fantastic.

    However, we didn’t come all this way just to stay on Earth. Let’s try Pluto this time. Pluto’s orbit is very irregular, but we can use ballpark numbers:

    Pluto semi-major axis, km: 5 874 000 000 km
    This is almost 40 AU, or 40 times the distance from the Sun to Earth.
    Pluto semi-major axis, m: 5 874 000 000 000 m 
    Pluto semi-major axis, binary integer: 101 0101 0111 1010 0101 1010 1110 1111 0100 0000 0000
    Length of Pluto-Sun distance integer: 42 bits

    42 bits is fewer than the 52 bits of precision we get in a double-precision number, so it looks pretty good. What kind of precision does 10 bits get us? 1 part in 512, or about 2 millimeters. That’s good positional precision.

    The problem is velocity. At 60 engine ticks per second, the slowest non-zero speed is 11 cm/sec.

    That’s not very good. Imagine you and your friend want to meet in orbit around Pluto. You show off your piloting skills by braking to park next to your friend. But instead of coming to a smooth stop, you’re slowing down in differences of about 0.4 km/h. If you bump into your friend gently, either your friend won’t move, or your friend will move quite a bit.

    Triple precision? Quadruple?

    Floating point numbers of arbitrary position exist, but they’re not for games. It’s all about what can be done in hardware.

    Most computers have 64 bit processors now; they can do many 64 bit math operations in one step. Obviously, 32-bit computers (or computers with 32-bit operating systems that can’t send 64-bit instructions to the CPU) are going to do these calculations more slowly. Slow is bad, and your game is spending a lot of time adding numbers when it could be doing things that the player would actually appreciate.

    But, you can fake extra precision. Faking things is really what space games are all about.

    Space Faking using Frames of Reference

    Reconsider this scenario: Two players are out by Pluto, trying to move slowly and finding that there’s insufficient precision. Where would there be sufficient precision?

    Closer to the Sun, of course. We already saw that 40 times nearer to the sun, everything is very precise. In fact, as we approach (0,0,0), we have up to 52 bits of precision, which is enough precision to store the size of individual protons. Yeah, seriously.

    Having the Sun be the center point of our position calculations makes calculating orbits very easy. But what if we kept two sets of coordinates?

    Keep one set where the Sun is at (0,0,0). Keep another set where the player is near (0,0,0).

    That way, the player’s coordinates are broken into two parts. One part is huge numbers that say, I’m near Pluto. Call this the reference frame. The other part is small numbers that say, I’m really close to my friend. Call this the local position.

    When the local position starts to get large, all we do is change the reference frame, by adding the local position to it. Then we reset the local position to 0, and we have 52 bits of precision again.

    At any point, a player’s position is their reference frame position plus their local precision. It doesn’t matter if your friend’s reference frame isn’t quite the same as your own when you’re trying to park next to each other, because all the changes are being applied to the local precision, and if you subtract each other’s reference frames first, all of the subsequent calculations deal with reasonably small numbers. When you separate and fly off to explore the universe, there’s no change required in each reference frame until you’re far apart — say, as far as the Earth is from the Sun. There might be a tiny jump in position at that time, relative to each other, but you’ll never see it.

    Simple, right?

    Some of these problems can be avoided by making your solar systems smaller or by choosing a game type that avoids small movements. But if you make those decisions, you’re accepting certain limitations going forward, and you have to remain conscious of them.

    Ignoring floating point numbers and how they work is a good way to walk straight into a jitter problem, where you know you’re doing the math right but the ship is jumping around from frame to frame.

    There is a big issue, not addressed here, related to the fact that most 3D rendering is only single-precision; 32-bit. But we can deal with that in another article.

    Good luck with your space game!

     
  2. Beacons Look Like Crap

    But we have beacons.

    No lookie until I get the lights installed.

     
  3. ARG

    Retrofitting the plumbing for an Entity Systems manager into my game is not as easy as I thought. 

    I want to work on features.

    1. Implementing ES gets me more features faster in the future. 
    2. Implementing ES gets me no features for a few days. 

    I’m usually fine with investing in architecture but I do it too much, and I throw away too much.

    ARG.

    Still thinking about this.

     
  4. Epic Learnings

    Ludum Dare 23 is in the voting phase now, which I’m doing as often as possible. I’ve had a ton of great feedback on Conqueror, and I’ve tried to be positive but constructively critical with other games. To paraphrase a tweet from @IcarusTyler, the longer my comment is, the more I liked your game.

    In between the warm-up weekend and the competition weekend, I got one evening of work in on my space game (which still needs a name). In that night, I implemented radar, sound effects, dramatically refined controls, and made the game more fun and playable. That’s way more than usual, and I was actually pretty tired.

    I think you can see where this is going.

    What I got from the competition was the chance to battle-test a few new skills, but what I caught from the competition was a really aggressive attitude.

    That attitude, in digestible form:

    • I am completely unwilling to let anything stand in my way of having a necessary feature completed;
    • I am completely intolerant of anything that is frustrating instead of fun (this was intensified greatly by reviews of Conqueror);
    • I remain completely committed to bringing awesome space adventure to everybody. 

    Both weekends, I started from a really tiny framework. My space project (which still needs a name) has a working multiplayer server, dynamic galaxies (plural! if needed), rough-but-tunable networked physics, a solid backstory and a modifiable player avatar.

    I have no idea what I’m waiting for. Drop a beacon! Set a message! Interstellar travel! Ship it! Play it! See what it needs next!

    I’ve tested a game architecture (Entity Systems) that lets me add features faster, and I love it. Great! Use it! Mick West at Cowboy Programming talks about retrofitting it into one of the Tony Hawk games, a component at a time. That works. No rewrites of the other code until they’re needed — the new architecture is completely compatible with what I’ve got now (in fact, it works better with the network layer than my existing code does).

    The problems I have to solve in my space game (which still needs a name) are a little more complex that my Ludum Dare games, but I have way more tools — an embarrassment, compared to what I was using for for the competition. I can break 90% of my to-do list into problems that are already 90% complete.

    The competition is done but for the cheering. I didn’t completely wipe out, so, I’m pretty sure I’m in for Ludum Dare 24 (unless my fingers fall off or get a complete ambitionectomy). That means a three month window of working on my space game (which still needs a name). And somehow, by means I’m still trying to comprehend, I did this last weekend, and this the weekend before. That’s 72 waking hours. I had no idea I could do that in 72 hours. I now have 640 hours before the next Dare to work on my space game (which still needs a name).

    I am so excited. This is going to be awesome.

     
  5. Here is a time lapse video of my Ludum Dare 23 entry, Conqueror, being made.

    I cut out most of the sleeping part. There wasn’t much of it anyway.

    The game is here. Play!