JoshJers' Ramblings

Infrequently-updated blog about software development, game development, and music

Posts tagged “development”

Floating Point Numbers and Rounding

I was writing about how to parse C++17-style hex floating point literals, and in doing so I ended up writing a bunch about how floats work in general (and specifically how floating point rounding happens), so I opted to split it off from that post into its own, since it’s already probably way too many words as it is?

Here we go!

How Floats Work

sign exponent mantissa 0 f e+f

If you don’t know, a floating point number (At least, an IEEE 754 float, which effectively all modern hardware supports), consists of three parts:

  • Sign bit – the upper bit of the float is the sign bit: 1 if the float is negative, 0 if it’s positive.
  • Exponent – the next few bits (8 bits for a 32-bit float, 11 bits for a 64-bit float) contain the exponent data, which is the power of two to multiply the given hex value with. (Note that the exponent is stored in a biased way – more on that in a moment)
  • Mantissa – the remaining bits (23 for 32-bit float, 52 for a 64-bit float) represent the fractional part of the float’s value.

In general (with the exception of subnormal floats and 0.0, explained in a bit) there is an implied 1 in the float: that is, if the mantissa has a value of “FC3CA0000” the actual float is 1.FC3CA0000 (the mantissa bits are all to the right of the decimal point) before the exponent is applied. Having this implied 1 gives an extra bit of precision to the value since you don’t even have to store that extra 1 bit anywhere – it’s implied! Clever.

The exponent represents the power of two involved (Pow2(exponent)), which has the nice property that multiplies or divides of a float by powers of two do not (usually, except at extremes) affect the precision of the number, dividing by 2 simply decrements the exponent by 1, and multiplying by 2 increments the exponent by 1.

For a double-precision (64-bit) float, the maximum representable exponent is 1023 and the minimum is -1022. These are stored in 11 bits, and they’re biased (which is to say that the stored 11 bits is actualExponent + bias where the bias is 1023. That means that this range of [-1022, 1023] is actually stored as [1, 2046] (00000000001 and 11111111110 in binary). This range uses all but two of the possible 11-bit values, which are used to represent two sets of special cases:

  • Exponent value 00000000000b represents a subnormal float – that is, it still has the effective exponent of -1022 (the minimum representable exponent) but it does NOT have the implied 1 – values smaller than this start to lose bits of precision for every division by 2 as it can’t decrement the exponent any farther and so ends up sliding the mantissa to the right instead.
    • For this 00000000000b exponent, if the mantissa is 0, then you have a value of 0.0 (or, in a quirk of floating point math, -0.0 if the sign bit is set).
  • Exponent value 11111111111b represents one of two things:
    • If the mantissa is zero, this is infinity (either positive or negative infinity depending on the sign bit).
    • If the mantissa is non-zero, it’s NaN (not a number).
      • (There are two types of NaN, quiet and signaling. Those are a subject for another time, but the difference bit-wise is whether the upper bit of the mantissa is set: if 1 it’s quiet, if 0 then it’s signalling).

If you wanted to write a bit of math to calculate the value of a 64-bit float (ignoring the two special exponent cases) it would look something like this (where bias in this case is 1023):

(signBit ? -1 : 1) 
  * (1 + (mantissaBits / Pow2(52 + 1)) 
  * Pow2(exponentBits - bias)

Standard Float Rounding

Okay, knowing how floats are stored, clearly math in the computer isn’t done with infinite precision, so when you do an operation that drops some precision, how does the result get rounded?

When operations are done with values with mismatched exponents, the value with the lowest exponent is effectively shifted to the right by the difference to match the exponents.

For example, here’s the subtraction of two four-bit-significand (3 bits of mantissa plus the implied 1) floats:

  1.000 * 2^5
- 1.001 * 2^1

The number being subtracted has the smaller exponent, so we end up shifting it to the right to compensate (for now, doing it as if we had no limit on extra digits):

  1.000 0000 * 2^5
- 0.000 1001 * 2^5 // Shifted right to match exponents
------------------
  0.111 0111 * 2^5
  1.110 111  * 2^4 // shifted left to normalize (fix the implied 1)
  1.111      * 2^4 // round up since we had more than half off the edge

Note that in this example, the value being subtracted shifted completely off the side of the internal mantissa bit count. Since we can’t store infinite off-the-end digits, what do we do?

Float math uses three extra bits (to the “right” of the mantissa), called the guard bit, the round bit, and the sticky bit.

As the mantissa shifts off the end, it shifts into these bits. This works basically like a normal shift right, with the exception that the moment that ANY 1 bit get shifted into the sticky bit, it stays 1 from that point on (that’s what makes it sticky).

For instance:

      G R S
1.001 0 0 0 * 2^1
0.100 1 0 0 * 2^2 // 1 shifts into the guard bit
0.010 0 1 0 * 2^3 // now into the round bit
0.001 0 0 1 * 2^4 // now into the sticky bit
0.000 1 0 1 * 2^5 // sticky bit stays 1 now

Note that the sticky bit stayed 1 on that last shift, even though in a standard right shift it would have gone off the end. Basically if you take the mantissa plus the 3 GRS bits (not to be confused with certain cough other meanings of GRS) and shift it to the right, the operation is the equivalent of:

mantissaAndGRS = (mantissaAndGRS >> 1) | (mantissaAndGRS & 1)

Now when determining whether to round, you can take the 3 GRS bits and treat them as GRS/8 (i.e. GRS bits of 100b are the equivalent of 0.5 (4/8), and 101b is 0.625 (5/8)), and use that as the fraction that determines whether/how you round.

The standard float rounding mode is round-to-nearest, even-on-ties (that is, if it could round either way (think 1.5, which is equally close to either 1.0 or 2.0), you round to whichever of the neighboring values is even (so 1.5 and 2.5 would both round to 2).

Using our bits, the logic, then, is this:

  • If the guard bit is not set, then it rounds down (fraction is < 0.5), mantissa doesn’t change.
  • If the guard bit IS set:
    • If round bit or sticky bit is set, always round up (fraction is > 0.5), mantissa increases by 1.
    • Otherwise, it’s a tie (exactly 0.5, could round either way), so round such that the mantissa is even (the lower bit of the mantissa is 0), mantissa increments if the lower bit was 1 (to make it even).

Okay, so if all we care about is guard bit and then , why even have three bits? Isn’t two bits enough?

Nope! Well, sometimes, but not always. Turns out, some operations (like subtraction) can require a left shift by one to normalize the result (like in the above subtraction example), which means if you only had two bits of extra-mantissa information (just, say, a round and sticky bit) you’d be left with one bit of information after the left shift and have no idea if there’s a rounding tiebreaker.

For instance, here’s an operation with the proper full guard, round, and sticky bits:

  1.000 * 2^5
- 1.101 * 2^1

// Shift into the GRS bits:
  1.000 000 * 2^5
- 0.000 111 * 2^5 // sticky kept that lowest 1
------------------
  0.111 001 * 2^5
  1.110 01  * 2^4 // shift left 1, still 2 digits left
  1.110     * 2^5 // Round down properly

If this were done with only two bits (round and sticky) we would end up with the following:

  1.000 * 2^5
- 1.101 * 2^1

// Shift into just RS bits:
  1.000 00 * 2^5
- 0.000 11 * 2^5
------------------
  0.111 01 * 2^5
  1.110 1  * 2^4 // shift left 1, still 2 digits left

Once we shift left there, we only have one bit of data, and it’s set. We don’t know whether or not we had a fraction > 0.5 (meaning we have to round up) or < 0.5 (meaning we round to even, which is down in this case).

So the short answer is: three bits because sometimes we have to shift left and lose one bit of the information, and we need at least a “half bit” and a “tiebreaker bit” at all times. Given all the float operations, 3 suffices for this, always.

Progress: Like Regress, Only Forward!

It’s been tricky to make much progress these last couple weeks – having a (non-gaming) coding job and being able to come home and work gets tricky, so a large majority of my game coding time is weekend time. Also, couple some deadlines at work, and you’ve got a large case of “I don’t want to code when I hit home.”

However: I did make a good deal of progress these last few weeks.

If you look at the screenshot in my last entry, it should be plain exactly HOW MUCH. Suddenly, my little experiment looks considerably like a GAME.

Particles Make Me Sneeze

The biggest hurdle for this section of the project was the general-purpose particle system. Even though I’ve done a bunch of crazy graphics-related stuff, a particle system has NEVER been on that list. But no longer!

For my particles, I wanted the following data:

  • Position (3D)
  • Rotation (around Z)
  • Image Index (which image in the particle map to use)
  • Scale (how big the particle is scaled, relative to the particle map’s specified world size)

The particle map mentioned in that list is a simple list of texture coordinates into the particle texture (Which contains images for all of the particles), as well as the size of a given particle in world space.

The particles in this system are actually rendered using shader constants (2 float4 shader constants per particle), which gave me right around 100 particles per draw call. On my PC, I can push the system up to 24,000 particles before it starts to slow down. On the Xbox 360, it’s closer to 6000. Both of those are well within my game’s target maximum of 2000 particles, and I could probably get that number higher if I had to.

The State Machine Tells Way Fewer Lies Than the Political Machine

One thing I learned when working on Mop of Destiny was how to set up a totally sweet state machine in C++. I got to port those concepts over to C#, which made it even EASIER, given all of the reflection support. Note that I do the majority of the reflection calls at application startup, so the expensive calls are already done when it’s time to do the actual game running.

Each state can have three functions associated with it: Begin, Tick, End.

Begin is called on the transition to that state from some other state.
Tick is called every time the state gets run (once per frame)
End is called on the transition from that state to some other state.

Also, each state can have a number of transitions associated. They take the form of: “BooleanFunction = TargetState”

Every frame, before calling tick, the state machine core will run each of the specified functions. When one of them evaluates to true, it switches states to the new TargetState, which will then be run (unless one of ITS transitions triggers). A state can also call the SetState function direction, but having the transitions in the function attribute makes it really easy to see where a state can transition to.

See You Later, Allocater!

One of the most important things that I have been doing with my code is ensuring that, during the run of a game level, no memory is allocated. At all. The reason is the .Net garbage collector (GC).

The GC, on Windows, is triggered every 2MB of allocations (among other scenarios, including low-memory and lost-focus cases). On the Xbox 360, the GC runs ever 1MB of allocations. Since the GC pauses all threads while it does its thing, it’s better that it’s never triggered during runtime…ESPECIALLY if the heap is complicated and garbage collection is going to take a while.

To handle this, I’ve created a few of my own data structures, including the OrderlessList. I’ve used OrderlessLists alot throughout my code. Simply stated, it’s an array (allocated at the time of the object with some maximum number of elements) in which the order of the objects is unimportant (i.e. it can be reordered and it doesn’t matter). Given the property of being able to reorder at any time, removal from the list is a simple matter of copying the last list over the top of the element being removed, then decreasing the reported size of the list.

For the bullets (both the player and the enemy bullets), there’s an OrderlessList of live bullets, and an OrderlessList of dead bullets. Whenever a bullet is needed, an object is retrieved from the dead bullet list, given its properties, and added to the live bullet list. Whenever a bullet dies (goes off-screen or hits an enemy), it is deactivated and returned from the live bullet list to the dead bullet list. No allocations necessary.

That’s right, it’s the ol’ “pool of objects so you don’t have to allocate” trick. But hey, it works!

Rambling Is For Cowboys, Not Coders

Alright, enough talk! Tomorrow is another day at work, so it’s likely I won’t make any more progress until next weekend.

In the meantime, death is but a door, time is but a window; I’ll be back.

The Topics Grew In Fields

A few things to talk about this entry (no screenshots, but there is an MP3 later):

Cube Farm

Cube farm is declared complete. I decided not to add any polish to it, because it was simply a test of my game development framework. It gave me a large list of things that I need to work on (better UI layout control, ability to put 3D objects into the UI, menuing systems, better input setup, etc) before moving on to the next game. Which brings us to…

Next Project

In keeping with my Konami-inspired game development theme, the next game that I’m planning will be a side-scrolling space shoot-em-up similar to Gradius.

The plan, however, is to have all of the entities and backgrounds be full 3D and make use of some of the higher-level features of my framework (some of which are actually implemented). These features include (but are not limited to):

  • Procedurally-generated textures (on-the-fly, cached in, no tiling)
  • instancing
  • Multi-core support
  • Procedural geometry

New Song

I was finally able to break through my composer’s block and get something (ANYTHING!) composed. I like it, though it’s a bit longer than it needs to be. Oh well, that I got anything written at all is good enough right now. I’ve been on a dry streak since the end of Mop of Destiny.

MP3: Longing – 4:31 (6.5 MB)

No Cows In the Cube Farm

Well, I finally have all of the gameplay working. It supports 2-5 players, and is quite enjoyable.

I need to add a, you know…menu, stuff like that. Also, I would love for it to play networked, since it does kinda require the cards in one’s hand be a secret to the other players.

But, the gameplay logic is all done, the last of the (known) bugs is squashed, and it’s time to go play Super Paper Mario!

BEHOLD MY RIDICULOUSLY UGLY SPECULAR GAME PIECES!


Click to enlarge

Chairless in Seattle

Cube Farm is coming along nicely – half of the gameplay is done.

In the game, for every turn, you place one card, and fill one cubicle (if possible). I have the card-placing portion done.

It involves filling in holes that are too small for a card to fit in as walkable floor space (for the purposes of determining if workers can reach the elevator). Any hole that has room for a card must remain open.

So now there’s a hand of cards to choose from, and you can select one and place it in a legal spot. Also done (though not visible), the code to determine which areas can reach the elevator and to determine how many points a given cubicle is worth is also in. Next up: worker placement!


Click to enlarge

More to do, more to do!

OMG WAI NO CHAIRS

The first test of my scripting setup is going rather well. And it’s completely chairless.

I have been working on converting over Cheapass Games’ “Cube Farm” into a digital format (which, sad to say, I’m never going to give out to anyone, what with copyright issues and the like. This is just my own internal test to make sure that I can do it), and it’s going rather well.

While I don’t have any of the scoring working yet, what I *DO* have is very promising. Namely, I have the cards displaying, and you can place them in the world.

Also, I recently added a blueprint-style background which, while it’s a bit higher contrast than I’d like, is a bit more interesting (and fitting) than the original green-on-black grid.

Some screenshots!



Click to enlarge

The camera control is simple (and intuitive): hold down the right mouse button, and you can drag the grid around (to move the camera). Holding middle mouse lets you move the mouse left/right to spin the camera around the look-at point, and up/down zooms in and out (and also adjust the angle at which you’re viewing the board).

So far, so good. Next up: Actual gameplay!

Crazy!

Do Androids Dream of Electric Chairs?

So, I’ve been working on the Intangibles. Otherwise known as the Unscreenshottables. Those things that improve the innards of the whole system, but you can’t really show off.

But first! A screenshot of something (to prove that my renderer can display more than just chairs):


Click to enlarge

One goal of this whole thing is that absolutely NO game logic will exist within the main EXE. It will all be loaded from script files (and eventually managed code assemblies).

I’ve gotten that up and running. The “scripting language” that I’m using is, in fact, C#. Basically, a script is an entity in the world, or an event, or any number of other things. They all derive from the IScript interface, which really requires three functions be implemented: OnCreate, OnTick, and OnKill.

I’ve exposed certain things to the scripting, like the math functions (vectors, matrices, quaternions), Camera control, texture/model loading, etc. There’s no direct access to the renderer, it’s all through the world data. You add objects into the world (which is setup by the main script), and it handles the rest.

I’m trying to make it as simple as possible.

Here is a sample script (for the rotating chairs that you have seen in videos past):

using System;
using Cubic.Scripting;
using Cubic.Math;

class RotatingObject : IScript
{
  IGameRenderable renderable;

  float angle;
  float rotSpeed;
  Vec3 position;
  static Random ran = new System.Random(190329);
  IScriptHelper helper;
  const float objectRange = 400.0f;

  public RotatingObject()
  {
  }

  public void OnCreate(IScriptHelper helper)
  {
    this.helper = helper;

    string[] filenames = new string[]
    {
      "ChairModel.xml",
      "ChairModel2.xml",
      "ChairModel3.xml",
      "ChairModel4.xml",
    };

    // Randomly pick a filename from the list
    string filename = filenames[ran.Next(filenames.Length)];
    angle = 0;

    // Choose a rotation speed
    rotSpeed = ((float)ran.NextDouble())*0.006f + 0.002f;
    if((ran.Next()&1) == 0)
      rotSpeed = -rotSpeed;

    // Random position in a cube
    position = new Vec3(((float)ran.NextDouble() * objectRange) - objectRange*0.5f,
                        ((float)ran.NextDouble() * objectRange) - objectRange*0.5f,
                        ((float)ran.NextDouble() * objectRange) - objectRange*0.5f);

    // Load the file (the true means to load synchronously, instead of caching the load for later)
    renderable = helper.CreateRenderable(filename, true);
    renderable.Position = Matrix.Translation(position);

    // Add it to the world (static update - which means it can move in place as long as
    // the bounding volume is sized big enough to cover the object's entire range of
    // motion
    helper.AddToWorld(renderable, ObjectUpdateType.Static);
  }

  public void OnTick()
  {
    // Rotate, and update the position matrix.
    angle -= rotSpeed * 5.0f*3.0f;
    renderable.Position = Matrix.RotationAxis(Vec3.Normalize(new Vec3(1, 1, 0)), angle)* Matrix.Translation(position);
  }

  public void OnKill()
  {
    helper.RemoveFromWorld(renderable);
  }
}

All-in-all, not terribly complex. It doesn’t have to deal with the renderer, or any major craziness. It just loads its object, adds it to the world, and goes about its thing.

There’s also a main script, which handles the initialization of the entire system (its filename is hardcoded into the exe – it is really about the only hardcoded thing in the system). Also, type scripts: these are scripts which are used as references by the other scripts, so the scripter can define custom types that can be used throughout the system.

There is currently no sound or network code, but graphics and input are working (well enough for now, anyway).

I still need to add actual mouse pointer support (instead of just mouse deltas), and I want to set up a menuing system, and then I’m going to work on scripting a little card game (which I will, sadly, not be able to distribute because it will be based off of an actual for-sale game, in this case Cheapass Games’ Cube Farm).

It should prove a nice little test of a simple game.

Also, I’ve been working on text and UI display, some elements of which I have working:


Click to enlarge

Yes, that screenshot is back to all-chairs. Chairs are a classic!

But enough talk. HAVE AT YOU!

80s Chair Band

I got bored and made a video of the chair demo. Why not? 😀

So here it is:

And, if you prefer to watch it in non-youtube-uglyvision, you can get a higher quality (6mb) WMV here:

So. Much. Chair. (I suggest you download rather than try to stream, it’ll go faster)

One Day, Maybe I'll Update With Some Regularity

Yeah, this journal does not get updated nearly as frequently as I’d like, but I’ve been working on a new basis for a few new games that I’d like to try and make (one of which is actually my old 3D racing game). In the meantime, I’ve been working on the backend of the whole thing, including the task management system (to take full advantage of multicore systems) and the renderer. Hopefully within a month I’ll be started on the Super Awesome Scripty System (TM) and the Ultra Mega Material Creation Tool (C).

In the meantime, I thought I’d share a screenshot from the stress test of the renderer. It was intended to test the culling system, to see how effective (and fast) it is, and the results are very promising.

It is the stuff of dreams, and the stuff of nightmares.

This….is…..CHAIRTOWN:

Once Upon A Time In Chairtown
Click to enlarge

Anyway, enjoy! Hopefully I’ll have some more interesting screenshots in the near future (though history certainly says otherwise).

Historeez

For those who missed it:

Mop of Destiny Soundtrack MP3s!

On Power Outages (And Other Weird Things)

They suck. Our power was out for 9 days. For someone as hopelessly addicted to the Internet as I, it was like not having legs. Legs that could span the globe in an instant. Or at least under a few seconds.

The power came back on, predictably, about 2 hours after we had left for the airport to go home to Indianapolis for the holidays.

Thus, the tally of weird things that have happened in the 6 months since I’ve moved to Seattle area and started work at Microsoft is:

  • My car (Which already had $3800 in hail damage) has been involved in 3 accidents, 2 of which were definitely not my fault and the third of which I can make a pretty good case for
  • Approached by a guy with a knife near Pike Place Market. He didn’t threaten me with it, he just wanted to sell it to me for $5.
  • Almost punched by an angry chauffeur in a stupid-looking suit at the Sea-Tac airport because I had the absolute audacity to try to help him out and tell him that the name on his little sign wasn’t visible.
  • There have been floods and even a (mild, for Indiana) snow storm
  • 9-day power outage

Life: Always entertaining.

Mop of Destiny: Last Words on Code Development

For the three (at most) of you that’s actually been waiting for the remainder of the info on Mop’s development history:

After getting the gameplay test done, I got around to actually loading level data. I ended up using TinyXML to load level data from an XML file.

Side note: Initially, each level was going to have a separate XML file, but that kinda got scrapped somewhere along the line. That’s why there’s 1 xml file named level1.xml in the game directory.

After that, I added fonts. I used a 3D modeler to make a 2D poly set of font characters, and then wrote a font loader to load them in and separate them out.

Essentially, the font interface allows you to request a string, where it then builds a vertex/index buffer set (a mesh) and hands that back. Simple and easy. And still no textures.

After that, it was adding animation code (simple: just different meshes for different frames of animation), the life meter (using a custom vertex shader to bend the meter partially around a circle), enemies, the knockback from getting hit, mesh-based instead of bounding-box-based collision, post process effects, ogg streaming, and spawners and enemy types.

At this point, I made a decision that would drastically alter my timetable, though I didn’t know it at the time:

I decided to make the enemy artwork while making the enemies.

The trick to this is that, in my initial plan, enemies were going to be coded as just boxes and the art would be added later. Not long into this process, I realized that having boxes was a terrible representation of the enemy, so I started doing the art, as well.

The enemy art was the second-hardest art-making process in the game (first being creating the main character). I had chosen shadow creatures partly to hide my own general inability to draw very well…with shadows I could simply draw an outline and some red eyes. However, it quickly became apparent that it was hard to create any INTERESTING shapes out of just outlines and red eyes.

Thankfully, I was able to do so. While I tried to keep to my inital development timeline, I didn’t really notice that I had moved alot of the art process into the “coding” block of schedule. Which meant that the schedule once I hit the “Art” portion was considerably lighter. Though I didn’t know it at the time.

Extra, Extra! Gameplay Complete! Game Still Ugly!

At last, I finished coding the bosses (which were the last enemies to be coded) and had the levels laid out (using solid blocks as backgrounds). The enemies looked cool, but the game looked ugly. A before-and-after, if you will:


VS

Click to enlarge

So I sent it out to a bunch of playtesters, none of which were particularly enthused about the game because of its inherent blockiness. Oh well. One person (thank you, PfhorSlayer!) played through the entire game, as punishing as it was at the time (you think it’s hard NOW? You should have seen it then).

Anyway, I did a bunch of tweaks, and started on the background art. That pretty much catches up to the journal-at-present.

From that point on, it was art, scripting, music, sound effects, voice, credits, the manual, some bug fixes, the installer, and a last-minute save state feature addition. All in the span of 14 days. It was, as they say, a whirlwind.

I’m really happy with the music. Excluding the first 2 pieces (Piano of Destiny and Theme of Destiny), which I spent a few days on (Because I had the melody in my head for so long, and I wanted to do it justice), the remaining music was done in two day’s time.

I used FL Studio as my editor of choice, using a bunch of sample libraries, notably Vienna Instruments.

Anyway, I plan on doing a full-on post-mortem as my next journal post.

In the meantime, TO THE XBOX 360!

FOR THE LOVE OF GOD POST AN ENTRY!

So I’ve been wanting to post more details about Mop of Destiny‘s development, as well as a full-on postmortem of the game. However, I took a long break from it (I needed it), and when I was getting back into it, I lost power last Thursday in the Great Pacific Northwest Windstorm and Subsequent Ginormous Blackout Of 2006 and have had no power at home since (almost a full week now), so it’s been tricky to do this. I’m typing this on my computer at work.

I still plan on posting that information, but in the mean time, I have a really crappy page set up with soundtrack MP3s from the game, ordered as I would have them if they were on a true soundtrack CD. You can nab them at:

http://mopofdestiny.com/ost/.

Also, there’s an updated EXE of Mop of destiny at http://mopofdestiny.com/MopInstall.exe that fixes an issue with Intel integrated cards that support hardware pixel shaders but not vertex shaders.

Enjoy! I shall return with light, triumphantly. Eventually.

PS – I’m in the planning stages of porting the game to XNA, so that it will eventually be playable on the Xbox 360. Woo!

Finishing Touches

Alrighty. The gameplay, graphics, art, music, and sound are all complete. I have a few finishing touches to put on it:

  1. Some of the background music has a different average level than the others, so I’m normalizing that all out
  2. I want to add the credits
  3. I need to provide a manual, so I’ve got to write that. Shouldn’t take long

.

But effectively, Mop of Destiny is complete, and should be showing up sometime tomorrow evening (Tuesday, PST).

Until then, here is the final boss music:

Final Boss!

Scripting

Scripting’s done. And I use the term “Scripting” lightly.

All “scripts” are hard-coded into the game. For instance, a sample short script would look like the following:

Add(new StopMusicPoint(m_audio));

// Whistler fades in.
Add(new CreateEntityPoint(this, m_audio, m_renderer, m_world, "Whistler", "Whistler", Vec2(14,1)));
Add(new LerpEntityVarPoint(this, "Whistler", "alpha", 0, 0.5f, 50));

Add(new DialogPoint(m_audio, m_renderer, 0xFF7FFF7F, 20, "Sound\\3-1.ogg",  " 'Nuther seal down, Jack?  Good for you!  Man, you're sweatin' like a pig...you look like you could use a towel."));
Add(new DialogPoint(m_audio, m_renderer, 0xFF7F7FFF, 20, "Sound\\3-2.ogg",  " Silly me for leaving home without it.  Arthur Dent would be so disappointed."));
Add(new DialogPoint(m_audio, m_renderer, 0xFF7FFF7F, 20, "Sound\\3-3.ogg",  " Who?"));
Add(new DialogPoint(m_audio, m_renderer, 0xFF7F7FFF, 20, "Sound\\3-4.ogg",  " Just a...nevermind."));
SkipTo();

Basically, that creates an entity named “Whistler” given the entity type “Whistler”, lerps its alpha value from 0 to 0.5, plays a bunch of dialog (the dialog point both plays the ogg dialog sound file and displays the text), then ends.

The SkipTo lets the script know that this is the point that it skips to when the user skips a cutscene. I’m not using it to make unskippable sections, it’s just a quick hack because I’m also using scripts to set up the background music at the beginning of each music zone, so I don’t want the player to be able to skip THAT.

Anyway, a couple of screenshots to tide you over. The first shows a script that’s running (along with the shopkeeper, Whistler P. Higgins), and the second shows the game running in wireframe (That’s right, I’m not using textures! For anything!)


Click to enlarge

Anyway, I hope to finish up the history details in an entry soon…I haven’t had much time to do so!

The Clocktower

The art for Mop of Destiny is now 100%.

Here are some shots from the final level of the game. Yes, you can jump on the gears, and yes, tracing the clock face took a very long time.


Click to enlarge

Today, I will start the cutscene work.

Lava Is Not My Favorite

Escape!

That is the goal of the fifth level. I have one screenshot that’s fairly representative of what the level looks like (there is no boss of this level).


Click to enlarge

Only one more level to go.

Shadow Realm

Level 4, the shadow realm. If there’s one thing that Super Mario Brothers taught me, it’s that all enemy fortresses HAVE to be made out of bricks.

I’m not happy with the brickwork in the palace (it’s a bit flat)…I’ll fix it later if I have time.

ON WITH THE SHOW!


Click to enlarge

Moving on to level 5!

Caves and Stuff

Didn’t quite finish level 3 last night, I got fed up of drawing all of the bevel borders on the rock platforms (it’s all hand-done). Finished it up this afternoon. Moving directly on to level 4.

An in-level screenshot, and a shot of the boss.


Click to enlarge

‘sall I got.

Ruins! Blocks! Ruins Made From Blocks!

Again, not much energy to say stuff. I remember what sleep was like. I remember it fondly.

Level 2 is not quite as varied in appearance as level 1 – it’s a bit narrower in focus.

Here are a couple screenshots. Jack’s standing on a falling block in the second shot.

Side note: Keep in mind, when viewing these screenshots that I have a strict “NO TEXTURES” policy. Every detail is a polygon. The idea was to have total resolution independence. There are a lot of polys in my little 2D sidescroller. …In retrospect however, maybe I should have just had textures.


Click to enlarge

Level 3 tomorrow! I hope to have the background art done by the end of the weekend.

…then all I have left is the cutscenes/music/sound. It’s a bit of an uphill battle to finish, but sometimes those are the most fun battles.

Trial By Fire.

15 days left. The gameplay is done and in “test” (a bunch of friends are playing through it). Thus, I had that terrifying moment where you send out your creation to the world and hope that it works.

…then someone reports that there’s a serious bug which you quickly fix.

That’s how it goes.

And now, TO THE WAYBACK MACHINE!

The Gameplay Prototype

So when last we spoke, I had set my schedule. I had just a few short weeks to get a complete gameplay prototype running. So I started where every game developer seems to start: With the graphics.

I mentioned before that the graphics were simple. Polygonal, no textures. Writing that took about a day (very, very simple code). I checked it in on August 12th.

Next up: Input. No sense in gameplay if you can’t PLAY it. I took the input code that I wrote for my old NES emulator and modified it slightly for my new uses. That gave me automatic keyboard/joystick control.

Side note: Never initialize DirectInput after initializing Direct3D, because it does bad, bad things to the window interface. DirectInput subclasses the window, which Direct3D doesn’t like, so D3D doesn’t like to restore the state of the window correctly after doing a Reset (i.e. for switching between fullscreen/windowed).

Blah blah blah, wrote a bunch of stuff, got the gameplay test done. Only a few days behind schedule, on September 4th.

A Note on the Art Path

So, my original “art path,” as it were, was amazingly complex.
It kinda went like this:

  1. Create object in AutoCAD.
  2. Export from AutoCAD to .3ds
  3. Use a 3D modeling package to import the .3ds
  4. Manually fix up the incorrectly exported colors
  5. Export to .ase
  6. Run custom converter from .ase (an easily parseable ASCII file format) to my own mesh format.
  7. Profit!

This eight-hojillion step process was a pain and, moreover, had one fatal flaw in it.

Check out step number 3. AutoCAD was incorrectly exporting the object colors.

As it turns out, AutoCAD has the ability to set truecolor values to objects, but it also has a built-in 256 color palette (likely left over from the olden days). Now, when ACAD would export, instead of exporting my delicious true colors, it would export the nearest match in the color palette. Consequently, I had to fix them up later.

This became a problem when I tried to do my first test background – Fixing up all of the colors was way too time-consuming, so I had to find a better way.

FIRST I tried to import the DXF directly into the 3D modeler. However, it ALSO screwed up the import.
SECOND I tried to write my own DXF reader. As it turns out, the object that I’m using as my building block (the REGION) is one of the only TWO types of ACAD object that are encrypted in the DXF. Which is stupid.
THIRD I found a third-party program to convert REGIONs into PolyLines, which I WOULD be able to read. However, this program also dropped the same color information I was trying to preserve, thus ensuring that every last person in the universe has screwed up the color import/export with ACAD files.

The Solution!

I found out that AutoCAD has its own API for writing plugins called ObjectARX. Essentially, I downloaded it and wrote an export function from AutoCAD directly into my mesh format. It does the following things: Scan the scene for regions, and for each region it finds, triangulate it (using ear clipping) then write that to the file.

So now, my art path has become:

  1. Create object in AutoCAD
  2. Export directly to my mesh format, with correct colors intact.

Much better.

MEDIA!!!

I don’t have any new screenshots. What I *DO* have are two of the songs from the game.

The first one is the song that will play during the main menu on game startup. It’s the piano version of the main theme.

Piano of Destiny

The second is the first-level music, which IS the main theme (thus, both songs have the same melody).

Theme of Destiny

Anyway, it’s amazingly late and I work tomorrow, so that’s all I have time for today. Backgrounds truly begin tomorrow!

Long time, no update!

So, wow. It’s been what…over a year? How time flies when life is throwing buckets at you.

Well, I’m going to start updating this journal with information on the (currently) continuing development efforts of my 4e5 entry entitled Mop of Destiny.

No, I haven’t given up on that racing game. Content creation got the better of me, but it’s not a dead project. I figured I had a chance to FINISH a 4e5 entry, so I switched gears a bit.

More info to come shortly (tonight).

Our Continuing Mission…

It is now highly apparent that there is no chance of me finishing this project by the end of this month. It was an impressively unrealistic goal in the first place. Oh well. It did its intended job: To light a fire under me and make me get to work on it.

And I’m still working. I don’t really have any pretty screenshots of anything new, but I’ve just finished planning some extensions to the gameplay (a totally different method of doing the collision detection that’ll be faster, as well as different track cross-sections, like driving inside of a tube). Then I’ll start beefing up the actual graphics engine proper. It’ll be nice to get some actual environments around those tracks!

Modeling will go as follows:

  1. Use track editor to create track
  2. Export completed track to a mesh format
  3. Load mesh into standard 3D Modeling program
  4. Create background
  5. Save/export to some format that I can read in
  6. Replace the temp materials assigned in the modeling program with the actual in-game materials
  7. Run it through the lightmap-generating radiosity dealy
  8. Save as level format
  9. Profit!

Or something like that.

Sound complicated? It is! But it’s easier than me coding my own 3D modeller. I tried that once, it didn’t work out so well.

Anyway, the AI is coming along…slowly. I’ve stopped developing it until after the collision detection rewrite.

Anyway, I guess the release date has changed a bit. It’s now “When It’s Done.” Except I plan on that not becoming the Duke Nukem Forever version of “When It’s Done.” (Note from 2024 blog port: I was technically correct about this but in the bad way)

This is still the most progress I’ve made on a game in my spare-time programming efforts. Ever. I’m happy with it!

Get Your Kicks On Loop 66

Finally! It took longer than expected (I had some issues with the collision detection), but I finally have the driving code working. That means, I can finally drive around these crazy tracks. As expected, because the tracks that I have were created before I even knew how the cars would handle or anything, not all of the turns/curves/loops are as forgiving as I’d like.

Some screenshots (note that the car is not actually going to be a sphere, but I needed something quick to represent the car):


Click to enlarge

Because the car is eventually going to be hovering, the driving control doesn’t have to be as accurate to real life as it would if the car had wheels. So I added some dampening to the sideways motion (Essentially lateral friction to keep the car from moving sideways, unless it’s skidding), but didn’t have to do a full-on friction model. Plus, the car has a force-field (or whatever they will call those things in the future), so the walls actually bounce the car more like a pinball bumper than in a realistic fashion (the force field applies some bounce force).

Anyway, now that I have the cars driving, it’s time for the next huge step: Driver AI.

Note that I have absolutely no idea at all how to write an AI to drive around my loopy sorts of courses. Time to do some research!

I’m Supposed To DRIVE on THAT?

Another week-plus break in journal entries. That’s starting to become a bad habit, but I don’t like updating when I don’t have anything to show.

Which brings us to today’s entry: I have something to show.

I created a new track editor. You may remember seeing screenshots of my previous track editor, way earlier in the journal. Those people who DO remember this are thinking to themselves, “That bonehead said that he would keep the existing editor because it works.” This is true. But, I decided that it was slightly hard (and by “slightly hard” I mean “nigh impossible”) to do some of the things that I really wanted to do fairly frequently (like spiral curves and full loops, to name a few). Plus, as the previous system was written using bezier patches , it took many patches to do any one feature (a loop generally took about 16-20 patches). This was lame.
>Since I am no longer using beziers as my basis for all terrain rendering, it’s no longer advantageous to use them exclusively in track creation. So I started over.


Click to Enlarge

Each track segment gets the following attributes (which are relative to the track segment’s initial orientation):

  • Length – This represents the angle of the track relative to the in-game representation of the north star. That, or just the length of the track down the middle. I can never remember which is which.
  • Width – Determines what the width will be by the end of the section of track (the previous section determines the initial width).
  • Curve – The amount of curvature of the track. A negative curve is to the left, positive is to the right. A 360 degree curve is, as expected, a full rotation.
  • Horizontal Skew – This is how much the track strafes to the side (along a sinusoidal sort of path) across its length. It’s like a lane-change. Only the pavement is changing lanes, not the car.
  • Incline Curve – This is the circular incline of the track. A 360 degree value of this would be a full loop. Positive is up, negative is down.
  • Vertical Skew – Similar to the horizontal skew, only vertical. How’s THAT for descriptive?
  • Twist – This is how much the track rolls to one side or the other along its length, which modifies the orientation for the next segment
  • Bank – This is similar to twist, except it raises one side instead of twisting around the middle, and the orientation remains unchanged in the next segment of track (this is to allow for banked curves, which I wouldn’t want to curve upwards because of the bank or something stupid like that).

Given those properties (And a semi-complicated way to combine them which I won’t detail here), I can still generate a track in segments. But I can use less segments to do the job.


Click to Enlarge

Once I got it generating, then I decided to make it considerably more adaptive. So it divides up the curves according to an error metric, which is why, in the last screenshot, some of the bits of track have fewer polygons (the straight bits only have one big one), and others have more.

The last thing to do is be able to export it to an actual mesh, so that I can load it up in a 3D modelling program and build the landscape around it.

But, for now, I can work on actually getting a car to drive on the track. Because now that I have a few levels, I want to find out what it’s like to drive on them!

Future Intrigue vs. the Wasteland of the Now

I promise that one day this journal will have something more entertaining than my I’m-Planning-On-Making-This-Cool-Game ramblings. In the interim, I present to you my general (and likely highly incomplete) plan of action (not including creation of art/level/music/sound assets, which will sort of happen as necessary):

  1. Get the graphics engine to the point where it can display a level, though without the greatest of visual quality (probably simple directional lighting, crap texturing, nothing that will make a pretty screenshot. It won’t look pretty, but it’ll work).
  2. Implement the driving-on-track code (i.e. keeping the vehicle on the track, bouncing/sliding against walls, controlling the vehicle)
  3. Implement some form of AI for the opponent cars. At this point, the game will be “playable” in a weak sense
  4. Begin implementation on the sound/music code. Get basic engine sounds, collisions, etc working.
  5. Begin to improve the graphics engine, by implementing the lightmap generator, getting new shaders written, particle systems, etc. Add some polish
  6. Begin work on the menuing system. Get a title screen/etc.
  7. Work on enemy AI. Make it drive better, make it tuneable (i.e. difficulty levels).
  8. If I’m going to do network code, I’ll probably want to start work on it right around here
  9. Start polishing the sound engine a bit (Add ability to have ambient sounds in the levels, doppler effect, all sorts of other audible cues)
  10. Get the graphics engine to the point that no more code changes will be necessary
  11. Begin fleshing out levels, start chaining them together into circuits
  12. More polish. Make the menus look cooler, tweak performance, all of that jazz
  13. At this point, the game will be highly playable, so anything past this is pure candy. HDR rendering? motion blur? If there’s time.

There are also some considerations that I will be attempting to make over the course of the whole development:

  • The game needs to be, above all else, fun. If I’m not having fun playing it, then why should I even be making it?
  • I am going to try my hardest to minimize load times. That is a very big priority for me. I hate waiting for games to load.
  • The driving control will be simple and easy. I have a great idea for mouse-driven steering (which may or may not be original, but I haven’t seen it personally) that I prototyped a while back…It’s definitely fun, but it needs tweaking. Probably in the form of a control options screen, with a good default setting.
  • The graphics code should be easily extendable. I’d like to be able to add higher-quality versions of the existing effects in the future (i.e. add some PS3.0 support for full-scale displacement mapping or such).

Hm. For the three of you out there that happen to keep tabs on this journal, if you see something (rather, fail to see something) important that I’m missing, please let me know. I’m new to this whole actually-trying-to-FINISH-a-game-in-this-lifetime thing.

Up, Up, and Away!

Well, my home computer’s hooked up, my Internet was just turned on, and the slate is clean.

Today’s mystery ingredient: Triangles!
Let’s see what the Iron Dev can dish out.

The story thus far…

One Point Five Years Ago

Early last year, I was playing around with a simple terrain engine. It was your basic height-field based terrain engine. It was also crap-tastic. But hey, you’ve gotta start somewhere. Thus, I started with this:


(Click To Enlarge)

Kinda lame, but it was a good test. Each chunk was LODified by using a Binary Triangle Tree, which was a great simplification method, though I did eventually switch to using progressive meshing (though leaving the edges in-tact…not the greatest LOD scheme ever). It was then that I had a vision. Well, not so much a vision, but an idea. Well, not even an idea. More of a thought. Cliffs! That’s right, cliffs. And caves. Those were two things that I would love to have, but they’re impossible with a straight-up heightfield renderer. So I set my tired code aside, and began my next crazy scheme.

Terrain Idea The Second!

So, I thought: what if I could model a level in a manner similar to making a topographical map. Something like this:


(Click To Enlarge)

In this system, each shape could be drawn, and given a height value. The editor would fill in the drawn shapes with polygons, connecting them to the other inner shapes (and outer shapes), adding slopes when asked (or making a given shape a plateau).

Sound crazy? It was. I coded it for about three months, working on logic to triangulate a complex polygon (overlapping not allowed) which contains children (which are holes in the parent polygon). Then I worked on smoothing the polygons by adding curves (see the first two screenshots for a comparison). Each edge was essentially a bezier curve, where the middle two control points were placed based on the angle of the connection to the next/previous line segments.

Now, right about now, you may be thinking to yourself: “How does this solve the problem of caves? How did you handle the LOD switching? Isn’t this a lot of work? What’s the meaning of life?” To answer your questions, in order: It doesn’t, I didn’t, Way too much, and free beer. This particular concoction was way too complex, and not near enough to what I wanted. So it, too, was scrapped.

The Not So Distant Past, Or: How to Totally Copy SSX’s Idea

I started browsing the web for other creative ways to generate terrain. It was then that I came upon THIS article. What a brilliant idea, I thought! Use curved surfaces to model terrain! So I started work on Terrain III: Terrain With a Vengeance. Not too long afterwards, I had a decent curved-surface terrain. The beziers were calculated in a vertex shader, given the matrix representation of the surface as a 4×4 matrix. Each vertex buffer simply contained the u and v coordinates (in the range [0, 1]) for the patch of the given resolution (different vertices in the vertex buffer were used for different tesselations). However, there were two big problems:

  • The vertex shader was somewhat slow, with the extra matrix multiplications (three 4×4 multiplications and a dot product for position alone)
  • There were cracks between the patches, due to floating point rounding errors.

So, I thought, what if I calculated the patches in software in a big dynamic vertex buffer, using a better method? I ended up using de Casteljau’s Method on the edges of the patches, because it does not have the same floating point issues that the matrix representation does. However, I continued using the matrix method on the interior points, for speed reasons. Each patch was cached into the vertex buffer using a cheap LRU cache. This eliminated the “sparkles” caused by the floating point errors, and significantly improved my framerate. And now, screenshots:



(Click To Enlarge)

This method worked well, but there were issues with locking/unlocking the buffer. Also, 800 patches meant 800 DrawPrimitive calls, which is, how you say, lame. Thus, I rebuilt the cache algorithm (Yes, I have the technology) to cache items into a set of smaller vertex buffers (that I called “slices”), which were each bound to a specific material (i.e. grass, etc). Thus, they could be batched up better. Soon, 800 patches meant only about 30 draw primitive calls. Which was way better, but there was still a problem. With great caching comes great framerate instability. Since the caching algorithm would sometimes not have to do anything, but sometimes it would have to cache a lot of patches, the framerate became very unstable. Even though it was running faster than 100 fps almost the entire time, it was still highly visible stuttering (especially when I fillrate-limited it a bit). Plus, there became the question of how to actually MODEL such a terrain (I was going to have to write a super-complicated terrain editor). So I wavered. It seemed that there were many, many problems with this implementation and that, while it’s definitely cool, I don’t know that I’m going to be able to do what I want within my newly-created timeframe. So I decided to set this method aside as well, and move on.

The Present, But Not the Birthday Kind

And that brings us to now. I’ve decided that I’m going to use a simple mesh format for the terrain, though with LOD data built into it. That way, I can use an existing modeling program (I’m looking at you, Blender) to do the modeling, build some simple texture/material/atmosphere editing utilities (or one texture/material/atmosphere editing utility), and just use that. Break it up into LOD-able chunks, and I think I have a winner!

Also, I’ll probably keep the track editing interface that I already have (though I’ll have to improve on it, obviously, since it’s…clunky, at best). But it provided me with these:


(Click To Enlarge)

Which are fairly representative of the types of tracks I’m aiming to have.

Alright, class, that’s the end of your history lesson for today. Up next: New developments in code!

This journal will self-destruct in 2.5 months.

Okay. I’ve set my deadline. The plans are in place. It’s now a matter of getting it done.

The Goal

A futuristic racing game. Similar to F-Zero. No weapons, no powerups, just racing. But I want it done by the end of October. Impossible, you say? Probably. But I’ve gotta try. And trying is the first step towards failure. Or something.

The Obstacles

I’m doing the whole thing myself. No artists, no musicians, no sound people, no budget. But I’m picky, so it still has to look good. I’m not very good at programming physics or AI, so I’ll have to brush up on those before I’m done. Textures will probably be mostly digital camera pictures edited to tile.

Components

  • Graphics
    • Terrain/Track
    • Foliage
    • Sky/Starfield
    • Clouds
    • Ocean Water
    • Vehicles
    • Shadows
    • Optional stuff, given time (HDR, motion blur, etc, etc)
    • Not to mention all of the modeling and artwork
  • Physics
    • Vehicle handling/control
    • Sticking to the track (even in loops/upside down areas)
    • Collision/Collision Response with track
    • Collision/Collision Response with other vehicles
  • AI
    • Getting AI to drive along track
    • Getting AI to avoid other vehicles
    • Advanced steering behaviors (skids, etc)
  • Sound
    • Doppler Effect
    • Environmental effects (echo, reverb, etc)
    • Background music
  • Input
    • Make Mouse/Keyboard control not suck
    • Enable joystick/wheel control
  • Network play (Given time)
    • Generic input structure to get input from AI, keyboard, or network sources
    • Research how to handle network play

Okay…that’s good enough for now, I think. This journal has helped me get a grasp on exactly how much I have to do. I definitely have my work cut out for me.

Side note: I don’t have internet access at home for the next few days, so updates will be less exciting than I would like for the next little bit.