3D Animation Techniques with XNA Game Studio 4.0

Exclusive offer: get 50% off this eBook here
3D Graphics with XNA Game Studio 4.0

3D Graphics with XNA Game Studio 4.0 — Save 50%

A step-by-step guide to adding the 3D graphics effects used by professionals to your XNA games.

£16.99    £8.50
by Sean James | January 2011 | Microsoft Web Graphics & Video

In this article, we will look at several ways to make the objects in our scene move. First, we will look at the animation of objects as a whole. We will do this through simple linear interpolation between start and end values, and through a more complex curve interpolation. We will also look at more complex animations through keyframed animation.

This article by Sean James, author of 3D Graphics with XNA Game Studio 4.0, covers:

  • Object animation
  • Keyframed animation
  • Curve interpolation

 

3D Graphics with XNA Game Studio 4.0

3D Graphics with XNA Game Studio 4.0

A step-by-step guide to adding the 3D graphics effects used by professionals to your XNA games.

  • Improve the appearance of your games by implementing the same techniques used by professionals in the game industry
  • Learn the fundamentals of 3D graphics, including common 3D math and the graphics pipeline
  • Create an extensible system to draw 3D models and other effects, and learn the skills to create your own effects and animate them
        Read more about this book      

(For more resources on 3D Animation, see here.)

Object animation

We will first look at the animation of objects as a whole. The most common ways to animate an object are rotation and translation (movement). We will begin by creating a class that will interpolate a position and rotation value between two extremes over a given amount of time. We could also have it interpolate between two scaling values, but it is very uncommon for an object to change size in a smooth manner during gameplay, so we will leave it out for simplicity's sake.

The ObjectAnimation class has a number of parameters—starting and ending position and rotation values, a duration to interpolate during those values, and a Boolean indicating whether or not the animation should loop or just remain at the end value after the duration has passed:

public class ObjectAnimation
{
Vector3 startPosition, endPosition, startRotation, endRotation;
TimeSpan duration;
bool loop;
}

We will also store the amount of time that has elapsed since the animation began, and the current position and rotation values:

TimeSpan elapsedTime = TimeSpan.FromSeconds(0);

public Vector3 Position { get; private set; }
public Vector3 Rotation { get; private set; }

The constructor will initialize these values:

public ObjectAnimation(Vector3 StartPosition, Vector3 EndPosition,
Vector3 StartRotation, Vector3 EndRotation, TimeSpan Duration,
bool Loop)
{
this.startPosition = StartPosition;
this.endPosition = EndPosition;
this.startRotation = StartRotation;
this.endRotation = EndRotation;
this.duration = Duration;
this.loop = Loop;
Position = startPosition;
Rotation = startRotation;
}

Finally, the Update() function takes the amount of time that has elapsed since the last update and updates the position and rotation values accordingly:

public void Update(TimeSpan Elapsed)
{
// Update the time
this.elapsedTime += Elapsed;

// Determine how far along the duration value we are (0 to 1)
float amt = (float)elapsedTime.TotalSeconds / (float)duration.
TotalSeconds;

if (loop)
while (amt > 1) // Wrap the time if we are looping
amt -= 1;
else // Clamp to the end value if we are not
amt = MathHelper.Clamp(amt, 0, 1);

// Update the current position and rotation
Position = Vector3.Lerp(startPosition, endPosition, amt);
Rotation = Vector3.Lerp(startRotation, endRotation, amt);
}

As a simple example, we'll create an animation (in the Game1 class) that rotates our spaceship in a circle over a few seconds:

3D Animation Techniques with XNA Game Studio 4.0

We'll also have it move the model up and down for demonstration's sake:

ObjectAnimation anim;

We initialize it in the constructor:

models.Add(new CModel(Content.Load<Model>("ship"),
Vector3.Zero, Vector3.Zero, new Vector3(0.25f), GraphicsDevice));

anim = new ObjectAnimation(new Vector3(0, -150, 0),
new Vector3(0, 150, 0),
Vector3.Zero, new Vector3(0, -MathHelper.TwoPi, 0),
TimeSpan.FromSeconds(10), true);

We update it as follows:

anim.Update(gameTime.ElapsedGameTime);

models[0].Position = anim.Position;
models[0].Rotation = anim.Rotation;

3D Graphics with XNA Game Studio 4.0 A step-by-step guide to adding the 3D graphics effects used by professionals to your XNA games.
Published: December 2010
eBook Price: £16.99
Book Price: £27.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on 3D Animation, see here.)

Keyframed animation

Our ObjectAnimation class allows us to create simple linear animations, but we can't create anything more complex. For example, we can't make our spaceship move in a circle with this class. To achieve more complex animations, we will use what is called keyframed animation. In this method, we specify "key" frames where we want the object to be in a specific position and orientation. We then rely on the code to interpolate between those values to fill in the frames between the key frames.

The following screenshot shows our spaceship at the keyframed positions along a path, and the black line shows the path that would be taken by interpolating between keyframes:

3D Animation Techniques with XNA Game Studio 4.0

Keyframed animation is useful because it is a fast way to create somewhat complex animations without having to animate each frame. For example, birds flying through the air, soldiers on patrol, or even a camera flying through a scene, can all be animated through keyframes. This is probably the easiest way to move the camera during a cutscene, for example. We represent a key frame with the ObjectAnimationFrame class. Like the previous class, it contains position and rotation values. It also, however, contains a time value, marking this frame's time offset from the beginning of the animation.

public class ObjectAnimationFrame
{
public Vector3 Position { get; private set; }
public Vector3 Rotation { get; private set; }
public TimeSpan Time { get; private set; }

public ObjectAnimationFrame(Vector3 Position, Vector3 Rotation,
TimeSpan Time)
{
this.Position = Position;
this.Rotation = Rotation;
this.Time = Time;
}
}

We can now create a new animation class that uses key frames:

public class KeyframedObjectAnimation
{
List<ObjectAnimationFrame> frames = new List<ObjectAnimationFrame>();
bool loop;
TimeSpan elapsedTime = TimeSpan.FromSeconds(0);

public Vector3 Position { get; private set; }
public Vector3 Rotation { get; private set; }

public KeyframedObjectAnimation(List<ObjectAnimationFrame> Frames,
bool Loop)
{
this.frames = Frames;
this.loop = Loop;
Position = Frames[0].Position;
Rotation = Frames[0].Rotation;
}
}

Finally, the Update() function figures out which frame we are on and interpolates between its values and the next frame's values, based on how far between them we are:

public void Update(TimeSpan Elapsed)
{
// Update the time
this.elapsedTime += Elapsed;

TimeSpan totalTime = elapsedTime;
TimeSpan end = frames[frames.Count - 1].Time;

if (loop) // Loop around the total time if necessary
while (totalTime > end)
totalTime -= end;
else // Otherwise, clamp to the end values
{
Position = frames[frames.Count - 1].Position;
Rotation = frames[frames.Count - 1].Rotation;
return;
}

int i = 0;
// Find the index of the current frame
while(frames[i + 1].Time < totalTime)
i++;
// Find the time since the beginning of this frame
totalTime -= frames[i].Time;

// Find how far we are between the current and next frame (0 to 1)
float amt = (float)((totalTime.TotalSeconds) /
(frames[i + 1].Time - frames[i].Time).TotalSeconds);

// Interpolate position and rotation values between frames
Position = Vector3.Lerp(frames[i].Position, frames[i + 1].Position,
amt);
Rotation = Vector3.Lerp(frames[i].Rotation, frames[i + 1].Rotation,
amt);
}

For example, we can now create a new animation to move our spaceship in a square:

KeyframedObjectAnimation anim;

We set it up as follows:

List<ObjectAnimationFrame> frames = new List<ObjectAnimationFrame>();

frames.Add(new ObjectAnimationFrame(new Vector3(-1000, 100, -1000),
new Vector3(0, MathHelper.ToRadians(-90), 0),
TimeSpan.FromSeconds(0)));
frames.Add(new ObjectAnimationFrame(new Vector3(1000, 100, -1000),
new Vector3(0, MathHelper.ToRadians(-90), 0),
TimeSpan.FromSeconds(3)));
frames.Add(new ObjectAnimationFrame(new Vector3(1000, 100, -1000),
new Vector3(0, MathHelper.ToRadians(-180), 0),
TimeSpan.FromSeconds(6)));
frames.Add(new ObjectAnimationFrame(new Vector3(1000, 100, 1000),
new Vector3(0, MathHelper.ToRadians(-180), 0),
TimeSpan.FromSeconds(9)));
frames.Add(new ObjectAnimationFrame(new Vector3(1000, 100, 1000),
new Vector3(0, MathHelper.ToRadians(-270), 0),
TimeSpan.FromSeconds(12)));
frames.Add(new ObjectAnimationFrame(new Vector3(-1000, 100, 1000),
new Vector3(0, MathHelper.ToRadians(-270), 0),
TimeSpan.FromSeconds(15)));
frames.Add(new ObjectAnimationFrame(new Vector3(-1000, 100, 1000),
new Vector3(0, MathHelper.ToRadians(-360), 0),
TimeSpan.FromSeconds(18)));
frames.Add(new ObjectAnimationFrame(new Vector3(-1000, 100, -1000),
new Vector3(0, MathHelper.ToRadians(-360), 0),
TimeSpan.FromSeconds(21)));
frames.Add(new ObjectAnimationFrame(new Vector3(-1000, 100, -1000),
new Vector3(0, MathHelper.ToRadians(-450), 0),
TimeSpan.FromSeconds(24)));

anim = new KeyframedObjectAnimation(frames, true);

3D Animation Techniques with XNA Game Studio 4.0

The Update code remains the same. Running the game, you will see the spaceship move from corner to corner of a box, turning towards the next corner at each stop.

3D Graphics with XNA Game Studio 4.0 A step-by-step guide to adding the 3D graphics effects used by professionals to your XNA games.
Published: December 2010
eBook Price: £16.99
Book Price: £27.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on 3D Animation, see here.)

Curve interpolation

We now have the ability to make animations with multiple key frames, which allows us to create more complex animations. However, we are still interpolating linearly between those key frames. This looks good for rotations, for example, but it would not look good for an object following a path, as the object would abruptly change direction after reaching a key frame in its animation. Instead, we want to be able to have our objects follow a smooth curve through the positions defined in the key frames. We will do this with what is called Catmull-Rom interpolation. This is a process that will create a curve through our key frame positions, allowing for much smoother object animation:

3D Animation Techniques with XNA Game Studio 4.0

Let's modify the KeyframedObjectAnimation class to use Catmull-Rom interpolation for the position value. XNA has a built-in function to calculate an interpolated position between the second and third points in a set of four points using Catmull-rom interpolation. However, it works only in one dimension, so we'll need to create a function that will interpolate between a set of instances of Vector3:

Vector3 catmullRom3D(Vector3 v1, Vector3 v2, Vector3 v3,
Vector3 v4, float amt)
{
return new Vector3(
MathHelper.CatmullRom(v1.X, v2.X, v3.X, v4.X, amt),
MathHelper.CatmullRom(v1.Y, v2.Y, v3.Y, v4.Y, amt),
MathHelper.CatmullRom(v1.Z, v2.Z, v3.Z, v4.Z, amt));
}

The amt argument specifies how far (0 to 1) between the second and third vectors the new position should be. We can now modify the position calculation to use this new function:

// Interpolate position and rotation values between frames
Position = catmullRom3D(frames[wrap(i - 1, frames.Count - 1)].
Position,
frames[wrap(i, frames.Count - 1)].Position,
frames[wrap(i + 1, frames.Count - 1)].Position,
frames[wrap(i + 2, frames.Count - 1)].Position, amt);

The wrap() function wraps the value that it is given around a certain interval—in this case [0, frames.Count – 1]. This means that we will not have to worry about our indices going out of range when finding the last point, next point, and so on, but it does mean that this type of interpolation will work best with a closed curve—a circle, for example:

// Wraps the "value" argument around [0, max]
int wrap(int value, int max)
{
while (value > max)
value -= max;

while (value < 0)
value += max;
return value;
}

We could now create the following keyframed animation with a curved path to demonstrate our new interpolation method:

List<ObjectAnimationFrame> frames = new List<ObjectAnimationFrame>();

frames.Add(new ObjectAnimationFrame(new Vector3(-500, 100, 1000),
new Vector3(0, MathHelper.ToRadians(0), 0),
TimeSpan.FromSeconds(0)));
frames.Add(new ObjectAnimationFrame(new Vector3(500, 100, 500),
new Vector3(0, MathHelper.ToRadians(0), 0),
TimeSpan.FromSeconds(3)));
frames.Add(new ObjectAnimationFrame(new Vector3(-500, 100, 0),
new Vector3(0, MathHelper.ToRadians(0), 0),
TimeSpan.FromSeconds(6)));
frames.Add(new ObjectAnimationFrame(new Vector3(500, 100, -500),
new Vector3(0, MathHelper.ToRadians(0), 0),
TimeSpan.FromSeconds(9)));
frames.Add(new ObjectAnimationFrame(new Vector3(-500, 100, -1000),
new Vector3(0, MathHelper.ToRadians(180), 0),
TimeSpan.FromSeconds(12)));
frames.Add(new ObjectAnimationFrame(new Vector3(-500, 100, 1000),
new Vector3(0, MathHelper.ToRadians(180), 0),
TimeSpan.FromSeconds(15)));
frames.Add(new ObjectAnimationFrame(new Vector3(-500, 100, 1000),
new Vector3(0, MathHelper.ToRadians(360), 0),
TimeSpan.FromSeconds(18)));

anim = new KeyframedObjectAnimation(frames, true);

3D Animation Techniques with XNA Game Studio 4.0

Summary

In this article we have covered the following concepts:

  • Object animation
  • Keyframed animation
  • Curve interpolation

Further resources on this subject:


About the Author :


Sean James

Sean James is a computer science student who has been programming for many years. He started with web design, learning HTML, PHP, Javascript, etc. Since then has created many websites including his personal XNA and game development focused blog http://www.innovativegames.net. In addition to web design he has interests in desktop software development and development for mobile devices such as Android, Windows Mobile, and Zune. However, his passion is for game development with DirectX, OpenGL, and XNA.

Sean James lives in Claremont, CA with his family and two dogs. He would like to thank his family and friends who supported him throughout the writing of this book, and all the people at Packt Publishing who worked hard on the book and to support him. He would also like to thank the XNA community for providing such amazing resources, without which this book would not have been possible.

Books From Packt


XNA 4.0 Game Development by Example: Beginner's Guide
XNA 4.0 Game Development by Example: Beginner's Guide

OGRE 3D 1.7 Beginner's Guide
OGRE 3D 1.7 Beginner's Guide

Away3D 3.6 Essentials
Away3D 3.6 Essentials

Unity 3D Game Development by Example Beginner's Guide
Unity 3D Game Development by Example Beginner's Guide

OpenSceneGraph 3.0: Beginner's Guide
OpenSceneGraph 3.0: Beginner's Guide

SketchUp 7.1 for Architectural Visualization: Beginner's Guide
SketchUp 7.1 for Architectural Visualization: Beginner's Guide

Papervision3D Essentials
Papervision3D Essentials

3D Game Development with Microsoft Silverlight 3: Beginner's Guide
3D Game Development with Microsoft Silverlight 3: Beginner's Guide


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software