Animation With Timers

Last time I talked about using timers for scheduling events by simply keeping track of a time limit and how much time elapsed. Another good use for timers is performing some action over time, such as modifying alpha to fade an object in or out, or sliding an object from one location to another.

Let’s use the fading example. Fading is often done by animating transparency. The object starts out completely transparent, and gradually becomes less transparent until it’s fully opaque. An object is fully transparent when it’s drawn using alpha blending with an alpha of zero, and fully opaque when alpha is one. By quickly changing alpha from 0 to 1 over time, and redrawing the object  with the new alpha, it appears to fade into view.

You might say that when alpha is 0 the object is 0% visible, and when alpha is 1 it’s 100% visible. Thinking about the starting and ending values as percentages is very useful. For example, If you’re moving an object from one position to another, the object hasn’t moved at all at 0%, it’s moved halfway at 50%, and it’s moved all the way at 100%.  Another example, if you’re fading in a song, it’s completely silent at 0% volume, it’s medium loudness at 50% volume, and it’s full volume at 100%. Being able to animate a value from 0 to 1 has all kinds of applications in a video game.

So we need to be able to animate a value from 0 to 1, and we want it to take a specified amount of time. In the last post we already made a timer that can run for a specified amount of time.

In that timer, the variable Elapsed keeps track of how much time has elapsed since the timer started. Elapsed starts out at 0, and as the timer runs it progresses to Limit, which is when the timer expires. Looking at it another way, Elapsed has progressed 0% when it starts, and 100% when it ends. In order to calculate the progress as a percentage, we simply divide Elapsed by Limit.

Let’s change the SimpleTimer class so it calculates this percentage for us. We’ll save it in a field called Time. Here’s the class as it stood at the end of the last post.

public class SimpleTimer
{
  public float Limit;
  public float Elapsed;

  public SimpleTimer(float limit)
  {
    Limit = limit;
    Stop(); // make sure it's not running
  }

  public void Start()
  {
    Elapsed = 0.0f;
  }

  public void Stop()
  {
    Elapsed = -1.0f;
  }

  public bool Update(float elapsedTime)
  {
    bool result = false;

    if (Elapsed >= 0.0f)
    {
      Elapsed += elapsedTime;
      result = Elapsed >= Limit;
    }

    return result;
  }
}

First, add our Time field right after Elapsed:

public float Limit;
public float Elapsed;
public float Time;    // <-- add this

Next, add an UpdateTime method that does the percentage calculation and saves it to the Time field:

private void UpdateTime()
{
  Time = Elapsed / Limit;
}

Now, every time we modify Elapsed we need to call UpdateTime so Time is recalculated. We set Elapsed to 0 in the Start method, so we need an UpdateTime call there:

public void Start()
{
  Elapsed = 0.0f;
  UpdateTime();  // <-- add this
}

We also modify Elapsed in the Update method:

if (Elapsed >= 0.0f)
{
  Elapsed += elapsedTime;
  result = Elapsed >= Limit;
  UpdateTime();  // <-- add this
}

And that’s it.  Here’s the full class:

public class SimpleTimer
{
  public float Limit;
  public float Elapsed;
  public float Time;

  public SimpleTimer(float limit)
  {
    Limit = limit;
    Stop();
  }
  public void Start()
  {
    Elapsed = 0.0f;
    UpdateTime();
  }
  public void Stop()
  {
    Elapsed = -1.0f;
  }
  private void UpdateTime()
  {
    Time = Elapsed / Limit;
  }
  public bool Update(float elapsedTime)
  {
    bool result = false;
    if (Elapsed >= 0.0f)
    {
      Elapsed += elapsedTime;
      result = Elapsed >= Limit;
      UpdateTime();
    }
    return result;
  }
}

Now, as our timer is running, it’s always calculating how far it’s gone towards Limit as a percentage, which gives us a value between 0 and 1.  If we’re animating something like alpha that already takes a value between 0 and 1 then we can just use Time directly. But what if we need to change from one color to another, or move an object from one position on the screen to another?

This is where interpolation comes into play. The dictionary defines interpolation as “the process of determining the value of a function between two points at which it has prescribed values”. The “prescribed values” are our starting and ending colors, or our starting and ending positions. There are multiple ways to do the interpolation depending on your needs. A common one is Linear Interpolation, and that’s what we’ll use here. I’m just going to describe a couple of methods supplied by the XNA framework. If you want to learn how linear interpolation works you should be able to find the information on the web pretty easily.

XNA provides several Lerp (Linear intERPolation) methods:

MathHelper.Lerp
Vector2.Lerp
Vector3.Lerp
Matrix.Lerp
Color.Lerp
Quaternion.Lerp

Each of these takes starting and ending values of the appropriate data type, an amount value between 0 and 1, and returns a new value that’s amount percent between the starting and ending values. For example, MathHelper.Lerp performs linear interpolation on float values. Here are some sample values and what the function returns:

MathHelper.Lerp(10, 20, 0.0f);  // returns 10
MathHelper.Lerp(10, 20, 0.2f);  // returns 12
MathHelper.Lerp(10, 20, 0.5f);  // returns 15
MathHelper.Lerp(10, 20, 0.8f);  // returns 18
MathHelper.Lerp(10, 20, 1.0f);  // returns 20

To move an object you can use the Vector2.Lerp method and pass in Time from our timer class as amount. In this example position will start at <50, 50> and work its way in a straight line towards <750, 400> as timer.Time changes from 0 to 1.

Vector2 start = new Vector2(50, 50);
Vector2 end = new Vector2(750, 400);
Vector2 position = Vector2.Lerp(start, end timer.Time);

Similarly, color can be interpolated between two values like this:

color = Color.Lerp(Color.CornflowerBlue, Color.White, timer.Time);

I’ve included a sample project that contains the timer class as well as a simple demonstration that moves a square across the screen and cycles colors using the timer and linear interpolation.

There are many more useful things you can add to your timers, such as pausing, reversing, auto-reversing, auto-restarting. It’s also very useful to add events that fire when the Time value updates, when the timer ends, and so on.

Download SimpleTimer.zip

Make Your Time!

It’s going on seven months since my last post, which is kind of pathetic. When finishing a large project, like the WP7 port of Guardian, my brain tends to shut down for awhile for recharging. That, on top of starting a new job back in November, lead to a much longer blogging hiatus than intended. So, how about something simple to get back into the swing of things. And since we’re talking about time…

Very often in video games there is a need to perform some action after a certain amount of time elapses. Examples include deciding when to show the next frame of an animation, deciding when to spawn the next enemy, hiding a message after it’s been displayed for awhile, and so on. For a most basic implementation of this we need two values: something to describe how long we want to wait, and something to track how long we’ve been waiting.

float timerLimit = 2.0f;
float timerElapsed = 0.0f;

We also need to pick a unit of time to work with. Seconds seem to be pretty convenient, and that’s what we’ll use here. So the value of 2.0 for timerLimit means we want to wait 2 seconds before doing something.

Once we have these values established it’s a matter of updating timerElapsed each frame and checking it against timerLimit to see if the desired amount of time has passed. Since the game programming API of choice here is XNA, the proper place to do this updating is in the game’s Update() method.

protected override void Update(GameTime gameTime)
{
  // grab the number of seconds that have elapsed since the last
  // frame, including fractional seconds, so if you're running
  // at 60fps this value will be 0.0166667 more or less, or
  // 1/60th of a second.
  float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

  // add the elapsed time to our timer
  timerElapsed += elapsedTime;

  // if the timer has exceed our limit then do something
  if (timerElapsed >= timerLimit)
  {
    // TODO : do something
  }
}

Once timerElapsed passes timerLimit then we can perform our action. There’s a problem with this code though. Once the timer exceeds our limit it will keep executing the action every frame thereafter. While this may be what we want sometimes, in many cases, if not most, it isn’t. So we need a way to turn off the timer, or restart it.

To restart it we can simply reset timerElapsed back to zero if we want to the event to fire again in the same amount of time. To stop the timer we can use timerElapsed as a flag and set to -1 to indicate that the timer is inactive. Our timer code now looks like this:

// update the timer if it's enabled
if (timerElapsed != -1)
{
  // add the elapsed time to our timer
  timerElapsed += elapsedTime;

  // if the timer has exceed our limit then do something
  if (timerElapsed >= timerLimit)
  {
    // TODO : do something

    // set the elapsed time back to 0 to schedule the event again,
    // otherwise set it to -1 to disable the timer
    timerElapsed = -1.0f;
  }
}

To start the timer initially our code needs to set timerElapsed to 0 which will being the process of tracking elapsed time until timerLimit is reached.

This code practically begs to become a class, so let’s not disappoint it.

public class SimpleTimer
{
  public float Limit;
  public float Elapsed;

  public SimpleTimer(float limit)
  {
    Limit = limit;
    Stop(); // make sure it's not running
  }

  public void Start()
  {
    Elapsed = 0.0f;
  }

  public void Stop()
  {
    Elapsed = -1.0f;
  }

  public bool Update(float elapsedTime)
  {
    bool result = false;

    if (Elapsed >= 0.0f)
    {
      Elapsed += elapsedTime;
      result = Elapsed >= Limit;
    }

    return result;
  }
}

Our previous example now looks like this:

SimpleTimer timer = new SimpleTimer(2.0f);
protected override void Update(GameTime gameTime)
{
  // grab the number of seconds that have elapsed since the last
  // frame, including fractional seconds, so if you're running
  // at 60fps this value will be 0.0166667 more or less, or
  // 1/60th of a second.
  float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

  if (timer.Update(elapsedTime))
  {
    // TODO : do something

    timer.Stop(); // or use timer.Start() to restart it
  }
}

Somewhat less crappy.

Scheduling events like this is one use for a timer. Another thing that’s often needed is the ability to perform an action over time, such as fading out text. The next post will cover that.

WP7 Planet Defender Progress Video

I’ve been working on a Windows Phone 7 port of Guardian and finally have something to show for it. All of the systems are in place now, just need to do a lot of tuning and game play tweaking. Of course, at this point I have no idea how it’s going to perform on an actual phone, but it should do fairly well. Hopefully there won’t be too much optimization required after I get my hands on some hardware.

I recorded the video using Fraps while running the version compiled for Windows. Keeping a version working in Windows has made testing and debugging work much more smoothly than having to deploy to the simulator each time I make a change. It’s also a fairly simple matter to simulate the touch functionality using the mouse.

Using CLR Profiler 2.0 with XNA 4.0

By default (at least on my computer, running Windows 7), CLR Profiler 2 won’t work with new XNA apps since they utilize the new .NET framework. It looks like it’s working when you try it, but when it shows the final statistics window it’s empty. Fortunately, it’s possible to enable support using the new profiler compatibility settings functionality, which for this case involves simply setting the COMPLUS_ProfAPI_ProfilerCompatibilitySetting environment variable before running CLR Profiler.

By default (at least on my computer, running Windows 7), CLR Profiler 2 won’t work with new XNA apps since they utilize the new .NET framework. It looks like it’s working when you try it, but when it shows the final statistics window it’s empty. Fortunately, it’s possible to enable support using the new profiler compatibility settings functionality, which for this case involves simply setting the COMPLUS_ProfAPI_ProfilerCompatibilitySetting environment variable before running CLR Profiler.

Here’s what I did:

Create a batch file called start_clr_profiler.bat with this content, save it in Documents or wherever:

c:
cd "program files (x86)clrprofilerbinariesx86
set COMPLUS_ProfAPI_ProfilerCompatibilitySetting=EnableV2Profiler
clrprofiler

It goes without saying that you’ll need to change the drive and path to where you have CLR Profiler installed.

Once you have this file, create a shortcut to it on your desktop. Right click on the shortcut, select Properties, click the Advanced button and check “Run as Administrator”. Click OK a couple of times, and you’re good to go. Just start the profiler with the batch file and the environment variable will be set, and the profiling magic will happen.

You could also set that environment variable at the user or machine level so it’s set all the time, but I don’t know what ramifications that has so prefer setting it just when needed. Happy garbage collecting!

Texture Modification in XNA 3.1

I’ve had a couple of questions about what changes are needed to get the texture modification tutorial to work in XNA 3.1.

So, here’s a 3.1 version of the project, and a quick overview of the major things that need to change.

  • You need to create the depth/stencil buffer yourself, set it on the GraphicsDevice when setting the render target, and restore the previous buffer when you’re done.
  • RenderTarget2D can’t be used directly as a texture, you must call RenderTarget2D.GetTexture instead and use that when drawing.
  • Render states are all set in GraphicsDevice.RenderState instead of the various classes used in 4.0.
  • Various minor syntax changes.

Some Sugar with your Syntax?

In the tutorial recently posted, I created the render state objects using some old-school syntax.

[csharp]
StencilAlways = new DepthStencilState();
StencilAlways.StencilEnable = true;
StencilAlways.StencilFunction = CompareFunction.Always;
StencilAlways.StencilPass = StencilOperation.Replace;
StencilAlways.ReferenceStencil = 1;
StencilAlways.DepthBufferEnable = false;
[/csharp]

You can do this in a much better way nowadays.

[csharp]
StencilAlways = new DepthStencilState()
{
StencilEnable = true,
StencilFunction = CompareFunction.Always,
StencilPass = StencilOperation.Replace,
ReferenceStencil = 1,
DepthBufferEnable = false
};

I’ve seen this syntax before, but 20 years of habits die hard and I rarely remember to use it. Hopefully it will stick now, and make my crappy code that much less crappy.

[/csharp]

In my recently posted tutorial, I created the render state objects using some old-school syntax.

StencilAlways = new DepthStencilState();
StencilAlways.StencilEnable = true;
StencilAlways.StencilFunction = CompareFunction.Always;
StencilAlways.StencilPass = StencilOperation.Replace;
StencilAlways.ReferenceStencil = 1;
StencilAlways.DepthBufferEnable = false;

You can do this in a much prettier way nowadays.

StencilAlways = new DepthStencilState()
{
  StencilEnable = true,
  StencilFunction = CompareFunction.Always,
  StencilPass = StencilOperation.Replace,
  ReferenceStencil = 1,
  DepthBufferEnable = false
};

That is so much nicer to read. I’ve seen this syntax before, but 20 years of habits die hard and I rarely remember to use it. Hopefully it will stick now, and make my crappy code that much less crappy.

Texture Modification using Render Targets, with some Stencil Buffer Action

Sometimes you need to modify a texture while your game is running, and there are a number of ways to do this. One of the first things newer game programmers often try to do is use Texture2D.GetData to copy the texture data from the GPU to an array on the CPU, modify the bytes, and then send it back to the GPU with Texture2D.SetData.

This is a bad idea on many, levels. Beyond issues with pipeline stalls, GetData and SetData can be slow, especially when working with a large texture. Any time you’re tempted grab data from the GPU for use on the CPU you should very carefully consider all of your options. There are often other solutions that let you keep the data entirely on the GPU and accomplish the same thing.

This tutorial will use an example that could be solved with GetData and SetData, and show you another alternative using render targets and the stencil buffer that will let you perform the same function entirely on the GPU.

Sometimes you need to modify a texture while your game is running, and there are a number of ways to do this. One of the first things newer game programmers often try to do is use Texture2D.GetData to copy the texture data from the GPU to an array on the CPU, modify the bytes, and then send it back to the GPU with Texture2D.SetData.

This is a bad idea on many levels. Beyond issues with pipeline stalls, GetData and SetData can be slow, especially when working with a large texture. Any time you’re tempted grab data from the GPU for use on the CPU you should very carefully consider all of your options. There are often other solutions that let you keep the data entirely on the GPU and accomplish the same thing.

This tutorial will use an example that could be solved with GetData and SetData, and show you another alternative using render targets and the stencil buffer that will let you perform the same function entirely on the GPU.

CPU Craters

Let’s pretend you want to draw 2D planet, and periodically add a crater to it. You want a hole to appear somewhere on the planet, so it looks like part of it was removed.

You could do this using the GetData/SetData method by getting the data from a texture into an array, setting the color to the background (or alpha to 0) in the shape of the crater, then writing the data back to the texture. Or you could be a little cleverer and eliminate GetData by always keeping the data in the array, but you still have to do the SetData to get it into the texture on the GPU each time it’s changed.

GPU Craters

The method we’ll use to do this entirely on the GPU involves several steps. First, we need a couple of resources. We’ll use a simple textured circle for a planet, and a crater shaped texture for the crater.

It’s important to note that the black areas on these have an alpha value of 0, meaning completely transparent. For the planet this just lets us draw the round shape over the background without looking like a square image. But for the crater image the alpha value is very important since it will control what part of the crater image is removed from the planet.

Next, we need to set up two render targets (these will be referred to later as Render Target A, and Render Target B). When we need to add a crater, one of these will be used as a target for drawing to, while the other used as a texture. The next time we add a crater they will swap roles – the texture will become the target, and the target will become the texture. This is called “ping-ponging” and will be discussed more fully later.

Once we have these resources ready to go, the method for adding a crater goes like this:

  1. Activate Render Target A using GraphicsDevice.SetRenderTarget.
  2. Clear the graphics device, setting the color to solid black, and the stencil buffer to 0.
  3. Set up the stencil buffer state so whatever we draw writes a value of 1 to the stencil buffer.
  4. Set up the alpha test state so we only draw where the alpha value is zero.
  5. Draw the crater texture. Because of the way we’ve set up the graphics device, only the parts of the crater texture that have alpha = 0 will be drawn, and those parts will write a 1 to the stencil buffer. So what we have at this point is a “mask” in the stencil buffer that we can use in the next step. The white area in the following image represents the stencil mask we’ve set up – the stencil buffer contains “1” in the white area, and “0” everywhere else.
  6. Set up the stencil buffer so when we draw, anything that has a value of 1 in the stencil buffer will be masked out – meaning it won’t draw.
  7. Draw the “planet texture”. Because of the way we’ve set up the graphics device, anything with a 1 in the stencil buffer won’t be drawn – since these 1’s are in the shape of a crater, that shape will be masked out of the planet texture, leaving holes that look like craters.
  8. Set the render target to the backbuffer. We can now access Render Target A as a texture, and that texture contains the planet texture with a crater-shaped hole in it.
Step 5
Step 7

From now on, until we need to add another crater, we can treat Render Target A as a texture and draw it using SpriteBatch, and we’ll have a nice crater. Now, what if we need to add another crater? This is where the ping-ponging comes in. Since Render Target A is now the “planet texture”, we need to be able to draw somewhere else when we’re filling in the stencil buffer with our crater shape. It just so happens that we set up another place to draw to, Render Target B.

So now, in Step 1, instead of activating Render Target A we need to activate Render Target B and draw the crater shapes into that. But what happens when we get to Step 7? Well, the “planet texture” is now in Render Target A, so we draw that. And in Step 8, Render Target B now contains our new planet texture with two craters.

And if we add a third crater then we’re back to where we started – drawing to Render Target A, and using Render Target B as the source texture. In other words, we “ping-pong” between the two render targets – each time we need to modify the texture, one is used for a texture, and one is used for drawing to, and then those roles are swapped.

You may have noticed that there’s one issue here. The first time through, Render Target B has nothing in it, so we can’t use it as the planet texture. This can be handled by using the actual planet texture the first time, and the render target thereafter.

The Code

Now let’s walk through the code involved, using XNA 4.0. You can do this in 3.1, but you’ll have to make significant changes when creating the render targets and setting the render states.

The complete code is in the downloadable project linked at the end of the tutorial. We’ll just go through the highlights here, referring to the steps mentioned above as we go.

The XNA 4.0 API has been changed substantially where render states are concerned, and for the better. Render states have been grouped by functionality into several classes. You create instances of these classes to represent the state you want, then set them on the graphics device, or pass them to SpriteBatch. So first we need to create these render state objects.

Set Up Render State Objects

For Step 3, we need to use the DepthStencilState class to set up the device to always set the stencil buffer to 1. We enable the stencil buffer, set the stencil function to Always, the pass operation to Replace, and ReferenceStencil to 1. This means that as we’re drawing, each pixel will Always pass, and the value in the stencil buffer will be Replaced with 1.

stencilAlways = new DepthStencilState();
stencilAlways.StencilEnable = true;
stencilAlways.StencilFunction = CompareFunction.Always;
stencilAlways.StencilPass = StencilOperation.Replace;
stencilAlways.ReferenceStencil = 1;
stencilAlways.DepthBufferEnable = false;

And for Step 4 we need to use the standard AlphaTestEffect so we can draw the asteroid texture only where the alpha value is 0.

Matrix projection = Matrix.CreateOrthographicOffCenter(0, PlanetDataSize, PlanetDataSize, 0, 0, 1);
Matrix halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);
alphaTestEffect = new AlphaTestEffect(GraphicsDevice);
alphaTestEffect.VertexColorEnabled = true;
alphaTestEffect.DiffuseColor = Color.White.ToVector3();
alphaTestEffect.AlphaFunction = CompareFunction.Equal;
alphaTestEffect.ReferenceAlpha = 0;
lphaTestEffect.World = Matrix.Identity;
alphaTestEffect.View = Matrix.Identity;
alphaTestEffect.Projection = halfPixelOffset * projection;

We first set up an orthographic projection matrix that matches SpriteBatch. We set AlphaFunction to Equal, and ReferenceAlpha to 0. This means the alpha test will pass whenever the alpha value we’re drawing is equal to 0. In our crater texture, the crater area has an alpha value of 0, while the surrounding area has 1, so only the crater area will be drawn.

For Step 6 we need a stencil buffer state that allows drawing only where the stencil buffer contains a 0. We enable the stencil buffer, set the stencil function to Equal, the pass operation to Keep, and the reference stencil to 0. This means that when we’re drawing, each pixel will pass if the value in the stencil buffer is Equal to 0.

stencilKeepIfZero = new DepthStencilState();
stencilKeepIfZero.StencilEnable = true;
stencilKeepIfZero.StencilFunction = CompareFunction.Equal;
stencilKeepIfZero.StencilPass = StencilOperation.Keep;
stencilKeepIfZero.ReferenceStencil = 0;
stencilKeepIfZero.DepthBufferEnable = false;

Create Render Targets

Now that we have the render state objects created, it’s time to create the render targets. Both are the same, so just one is shown here. This creates a render target with a Color format, and a depth format that includes a stencil buffer.

renderTargetA = new RenderTarget2D(GraphicsDevice, PlanetDataSize, 
  PlanetDataSize, false, SurfaceFormat.Color, 
  DepthFormat.Depth24Stencil8, 0, 
  RenderTargetUsage.DiscardContents);

Draw the Crater Mask

Next up is drawing the crater masks (Steps 2-5). First we activate the render target, clear it to solid black, and clear the stencil buffer to 0.

GraphicsDevice.SetRenderTarget(activeRenderTarget);
GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.Stencil,
                     new Color(0, 0, 0, 1), 0, 0);

Next we begin a SpriteBatch, passing in the stencilAlways and alphaTestEffect objects that we created earlier. Calculate some random rotation, size the crater texture using a Rectangle, and call SpriteBatch.Draw to draw the crater.

spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque,
                  null, stencilAlways, null, alphaTestEffect);
Vector2 origin = new Vector2(craterTexture.Width * 0.5f,
                             craterTexture.Height * 0.5f);
float rotation = (float)random.NextDouble() * MathHelper.TwoPi;
Rectangle r = new Rectangle((int)position.X, (int)position.Y, 50, 50);

spriteBatch.Draw(craterTexture, r, null, Color.White, rotation,
                 origin, SpriteEffects.None, 0);

spriteBatch.End();

Draw the Planet Texture

Now we need to draw the latest planet texture, using the stencil buffer to mask out the craters (Steps 6-7). We begin a SpriteBatch, passing in the stencilKeepIfZero object we created earlier. Note that the first time we draw the actual planet texture, but subsequently we draw using the texture from the previous iteration.

spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque,
                  null, stencilKeepIfZero, null, null);

if (firstTime)
{
  spriteBatch.Draw(planetTexture, Vector2.Zero, Color.White);
  firstTime = false;
}
else
  spriteBatch.Draw(textureRenderTarget, Vector2.Zero, Color.White);

spriteBatch.End();

Swap Render Targets

Finally we activate the backbuffer render target.

GraphicsDevice.SetRenderTarget(null);

And then swap the render targets as discussed previously.

RenderTarget2D t = activeRenderTarget;
activeRenderTarget = textureRenderTarget;
textureRenderTarget = t;

In the main Draw function, you draw the latest cratered planet using the textureRenderTarget. Of course, you need to deal with using the planet texture the first time through though. The downloadable code shows one simple way to do that.

GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(textureRenderTarget, planetPosition, Color.White);
spriteBatch.End();

Conclusion

And there you have it, a powerful technique for altering textures during your game. Doing this entirely on the GPU is quite a bit more complex than GetData/SetData, but is well worth the extra trouble.

There are some things you can do to improve this technique. If you need to add a lot of craters, rather than adding them one at a time you can batch them up for a while, then in Step 5 draw all of them at once.

I hope you found this tutorial informative. Learning about render targets and stencil buffers opens up a whole new world of possibilities beyond just making craters. What other uses can you think of?

Download the sample XNA 4.0 project

Download the sample XNA 3.1 project

Sprite Sheet Creator

When developing the iPhone version of Guardian I manually created my sprite sheets. I used individual sprites up until the end so everything was pretty much set in stone by the time I created the the sprite sheet. Even then I ended up having to recreate the sprite sheet two or three times, and let me tell you, manually figuring out the texture coordinates isn’t a particularly pleasant experience. In this case I believe I made the right choice. There were few enough sprites that I would have spent more time creating the tool than I would have saved.

When developing the iPhone version of Guardian I manually created my sprite sheets. I used individual sprites up until the end so everything was pretty much set in stone by the time I created the the sprite sheet. Even then I ended up having to recreate the sprite sheet two or three times, and let me tell you, manually figuring out the texture coordinates isn’t a particularly pleasant experience. In this case I believe I made the right choice. There were few enough sprites that I would have spent more time creating the tool than I would have saved.

The XBox version has quite a few more sprites, so I decided that spending time creating a sprite sheet tool was going to be well worth the effort. It didn’t take too long to get it working well enough to use, and not too much longer than that to make it solid enough for distribution.

Sprite Sheet Creator

The application is released as open source under the MIT License.

Download SpriteSheetCreator.zip

Sprite Splitting with SpriteBatch

Someone over in the XNA forums asked a question about how to make sprite explosions like those in the old Defender arcade game, where the sprite is broken into pieces and exploded everywhere.

Someone over in the XNA forums asked a question about how to make sprite explosions like those in the old Defender arcade game, where the sprite is broken into pieces and exploded everywhere.

This effect can be done using nothing more than the XNA SpriteBatch class. One of the overloaded Draw() methods allows you to pass a source rectangle. When drawing a sprite you can use the source rectangle to grab just a part of it. So it’s a simple matter to use multiple draw calls on a single sprite to draw little pieces of it, like so:

// draw parts of the sprite
int xInc = 8;
int yInc = 8;
float spacing = 1.5f;

// draw parts of the sprite
for (int x = 0; x < face.Width; x += xInc)
  for (int y = 0; y < face.Height; y += yInc)
  {
    Vector2 position = new Vector2(100 + x * spacing, 150 + y * spacing);
    Rectangle source = new Rectangle(x, y, xInc, yInc);
    spriteBatch.Draw(face, position, source, Color.White);
  }

“Multiple draw calls” sounds bad, but SpriteBatch is able to batch up the draw calls so you shouldn’t notice any real effect on performance.

You can download a sample XNA project to see this in action. The project also includes a SpriteExploder class that will automatically explode your sprite into multiple pieces and throw them about the screen.

screenshot

Download Sample Project

Horizon Culling 3 – Finale

So we finally come to the last post in the horizon culling series. Previously we’ve discussed what horizon culling is and some reasons for using it. Then we went through the math involved in determining the angle between a line from the camera to the planet center and a line from the center to the point on the horizon.

So we finally come to the last post in the horizon culling series. Previously we’ve discussed what horizon culling is and some reasons for using it. Then we went through the math involved in determining the angle between a line from the camera to the planet center and a line from the center to the point on the horizon.

To help me visualize everything up to this point I created a standalone Windows application. Please feel free to download the app which includes a binary and full C# source code. You can change the height of the camera to see how the horizon distance and angle change.

The calculations we discussed in the last post are handled in the Recalculate method. This method is called every time the slider is moved. All of the drawing is performed in the Paintbox_Paint method. The drawing code is very ugly, but since this was just intended as a simple, one time method of visualizing the math, I didn’t bother spending much time making it pretty.

So, now that we have this angle, how do we use it? The basic idea is that as we draw each portion of the planet we calculate the angle between the camera to planet center line and the portion of the planet we’re drawing. If that angle is larger than the horizon angle then what we’re drawing isn’t visible to the camera so we don’t need to draw it.

In reality it isn’t quite as simple as this because the part of the planet we’re drawing may have mountain where the base is beyond the horizon, but the top would still be visible since it extends above the horizon. There are multiple ways to handle this. For my purposes, at least for now, I chose the lazy man’s way and just increase the horizon angle a bit so I draw things beyond the horizon.

I don’t want to go into a lot of detail here, but I need to describe in broad terms how I go about drawing a planet. Basically, the planet is divided into patches. When you’re far away from the planet the patches represent a very large part of the surface, and as you move closer the patches closest to you are split into more detailed patches. So as I draw the planet, patches that are far away have much less detail in them than patches that are close by. These patches are the lowest granularity for drawing the planet surface. In other words I can’t draw part of a patch – either the entire patch is drawn, or none of it is.

So that means the patches are what we’ll be culling against the horizon, and to do that we have to use the patch position to find out what angle the patch is relative to the camera. The image below should help clarify what I’m talking about.

horizon_culling_61

The thick green line represents a patch that is visible to the camera. The thick red line is a patch that is beyond the horizon so it isn’t visible. When horizon culling these patches we need to determine the angle between the camera (C) and the point on the patch that is nearest the camera (P1 and P2). When determining P1 and P2 I actually only check the 4 corner points on the patch.

So how do we determine the angle? The dot product of two unit length vectors gives us the cosine of the angle between them. We can use this property to easily find the angle between the camera position and the patch position. For our purposes a point and a vector are in effect the same thing.

First we have to translate each point into planet space. I’m defining planet space as a coordinate system where the center of the planet is at the coordinate 0, 0, 0. For the camera it’s camera_position – planet_position. For P1 you may have to do something different depending how your data is organized. For mine, the patch itself has a position that’s already in planet space, and each point in the patch is in patch space. So to get the closest point into planet space it’s the patch_position + closest_point_position. Again, your mileage may vary, but the ultimate goal is that both points have to be in planet space.

Now, the other thing that’s required for the dot product to work is that each vector has to be unit length – i.e. it must have a length of one. To do that we have to normalize the vector, which simply involves calculating the length of the vector, then dividing each component (x, y, z) by the length. We’ll be using the built in XNA functionality for normalizing a vector.

Once we have the two vectors in the same space and unit length we can do the dot product operation to give us the cosine of the angle. To get the angle we use the arccos function as we have before.

So, here are the steps using C#, and the XNA vector class.

// get camera position and patch position in planet space
Vector3 C = CameraPosition - PlanetPosition;
Vector3 P1 = PathPosition + ClosestPatchPoint;

// normalize each vector
C.Normalize();
P1.Normalize();

// calculate the angle between the two vectors
double Angle = MathHelper.ToDegrees((float)Math.Acos(Vector3.Dot(C, P1)));

Again, we get both points into planet space, normalize each, then calculate using the arccos of the dot product.  And since my brain still works in degrees, we use an XNA helper function to convert the angle.

Now that we have that angle, we can compare it to the horizon angle. If this angle is greater than the horizon angle then we can skip drawing the patch. I mentioned earlier that you need to tweak the horizon angle a bit to account for mountains. I just add 5 degrees to the calculated angle. When very far away from the planet (over 1000 miles) I also add another 25 degrees to account for the very very large patch sizes. I arrived at these values through observation of an Earth-sized planet and they would likely need to be tweaked for different sized planets. At some point I would want to come up with an algorithm to deal with these things automatically, but they work fine now for my current needs.

There are probably more formal and accurate ways of doing horizon culling to more properly deal with mountains and such, but in practice this way requires very little extra processing and the few extra (but unnecessary) patches we draw from time to time have a fairly negligible impact on performance.

So, I think that does it for this series. Hopefully I’ve provided a clear enough explanation of this technique that you can use it in your own projects. If not that, then the math techniques have many applications as well.

Download Sample Project