XNA Essentials

Game Programming for Xbox 360, PC + Windows Phone

NAVIGATION - SEARCH

Shake that Camera

I was browsing the educational catalog on the App Hub and I saw something that I had missed previously.  I wanted to point it out and also point out that if you haven’t been to the educational catalog section in a while, you should go browse it for some nice examples.

The little demo I saw was one that shakes the camera.  It also vibrates the controller (or the Phone).  It was pretty easy to take the important pieces of code and modify the Camera object I used in the XELibrary (the library the book uses).

 

Inside the Update method of the game class (i.e. Game1 inside of Game1.cs) you can add the following code:

if ((input.ButtonHandler.WasButtonPressed((int)PlayerIndex.One, Buttons.A) || (input.KeyboardState.WasKeyPressed(Keys.Space))))
{
    camera.Shake(25f, 2f);
}

 

Next, we need to actually create that method (Shake) on the camera.  Since we would want to use this on the FirstPersonCamera and the static Camera we will apply it to the base class Camera.  Add the following variables to Camera.cs:

// We only need one Random object no matter how many Cameras we have
private static readonly Random random = new Random();
 
// Are we shaking?
private bool shaking;
 
// The maximum magnitude of our shake offset
private float shakeMagnitude;
 
// The total duration of the current shake
private float shakeDuration;
 
// A timer that determines how far into our shake we are
private float shakeTimer;
 
// The shake offset vector
private Vector3 shakeOffset;

 

Next, add the following two methods to the Camera.cs file:

/// <summary>
/// Helper to generate a random float in the range of [-1, 1].
/// </summary>
private float NextFloat()
{
    return (float)random.NextDouble() * 2f - 1f;
}
 
/// <summary>
/// Shakes the camera with a specific magnitude and duration.
/// </summary>
/// <param name="magnitude">The largest magnitude to apply to the shake.</param>
/// <param name="duration">The length of time (in seconds) for which the shake should occur.</param>
public void Shake(float magnitude, float duration)
{
    // We're now shaking
    shaking = true;
 
    // Store our magnitude and duration
    shakeMagnitude = magnitude;
    shakeDuration = duration;
 
    // Reset our timer
    shakeTimer = 0f;
}

The first method is just a helper method we need soon.  The second method is the public Shake method on our camera that initializes the values so we can begin shaking the camera’s view.

Finally, in the Update method of Camera.cs add replace the following code at the bottom of the method:

Matrix.CreateLookAt(ref cameraPosition, ref cameraTarget, ref cameraUpVector,
   out view);

With the following code:

// If we're shaking...
if (shaking)
{
   // Move our timer ahead based on the elapsed time
   shakeTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
 
   // If we're at the max duration, we're not going to be shaking anymore
   if (shakeTimer >= shakeDuration)
   {
       shaking = false;
       shakeTimer = shakeDuration;
   }
 
   // Compute our progress in a [0, 1] range
   float progress = shakeTimer / shakeDuration;
 
   // Compute our magnitude based on our maximum value and our progress. This causes
   // the shake to reduce in magnitude as time moves on, giving us a smooth transition
   // back to being stationary. We use progress * progress to have a non-linear fall 
   // off of our magnitude. We could switch that with just progress if we want a linear 
   // fall off.
   float magnitude = shakeMagnitude * (1f - (progress * progress));
 
   // Generate a new offset vector with three random values and our magnitude
   shakeOffset = new Vector3(NextFloat(), NextFloat(), NextFloat()) * magnitude;
 
   // If we're shaking, add our offset to our position and target
   cameraPosition += shakeOffset;
   cameraTarget += shakeOffset;
}
 
Matrix.CreateLookAt(ref cameraPosition, ref cameraTarget, ref cameraUpVector,
   out view);

 

So whe the main game class calls camera.Shake, the flag shaking becomes true and the shaeMagnitude, shakeDuration and shakeTimer are set.  This way when the camera game component has its Update method called, it sees that it needs to shake the camera and adds the game’s total seconds to the shakeTimer and if it has already been shaking too long it sets it to false and resets the shakeTimer.  Assuming it hasn’t been shaking long enough (or even started)  it computes the progress and determines the magnitude of the shaking from that value.  It then uses that magnitude to create an offset with some randomness.  The cameraPosition and cameraTarget then has the offset added to it.  This is what makes the camera shake.  When the view is calculated again it has new values for the camera position and target.

So just a little bit of code allows us to create a cool shaking effect in our games.  Nice.

Happy Coding!

-Chad