# Like 5Likes Dislike 3D Animation Techniques with XNA Game Studio 4.0

frames position new timespan rotation vector3 mathhelper vector3(0 add(new

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
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:

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("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;
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:

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 frames = new List();
bool loop;
TimeSpan elapsedTime = TimeSpan.FromSeconds(0);

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

public KeyframedObjectAnimation(List Frames,
bool Loop)
{
this.frames = Frames;
this.loop = Loop;
Position = Frames[0].Position;
Rotation = Frames[0].Rotation;
}
}
Finally, the Update() function fï»¿igures 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	  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 frames = new List();

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);

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.

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:

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	 value += max;
return value;
}
We could now create the following keyframed animation with a curved path to demonstrate our new interpolation method:

List frames = new List();

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);

Summary
In this article we have covered the following concepts:
• Object animation
• Keyframed animation
• Curve interpolation

### 5 Comments

/ EndlessSporadic
May 08 2012 06:36 PM
What do the following two lines mean? They are incorrect syntax.

1) while(frames[i + 1].Time i++;
2) while (value value += max;
Sep 28 2012 09:48 AM
while( value )
{
// some code here
}

checks if the value is true (boolean) and executes the code between the brackets while this is the case.

It constantly does: check - execute - check - execute - check - execute -... until value turns false
May 15 2013 04:37 AM

I haven't implemented it yet, but is seems excellent!

Good Job!

May 15 2013 02:08 PM

I've implemented it successfully!

Great job! Again!

/ chloe1234
May 25 2015 01:56 AM

Correction: nike roshe run Seattle north face Minimum nike roshe Wage vans story lululemon outlet online Associated herve leger Press

Seattle wedding dresses begins ray ban sunglasses to nike trainers phase louboutin in hollister $15 montre homme minimum coach factory outlet wage lululemon outlet Associated coach outlet store online Press What air jordan we can converse learn oakley pas cher from sac hermes the mont blanc CEO rolex watches who babyliss pro took louis vuitton a abercrombie and fitch 93% chi flat iron pay links of london cut nike blazer to reebok outlet give swarovski his soccer shoes team vanessa bruno a valentino shoes raise jordan shoes Vox.com Seattle's louboutin shoes Wage birkin bag Hike tory burch outlet Reminds michael kors pas cher Us wedding dresses A instyler$9 toms shoes Minimum nike free Wage vans shoes Isn't sac burberry Enough prada handbags Huffington oakley sunglasses Post

US nike shoes CEO louboutin cuts hollister salary oakley sunglasses to converse pas cher boost converse shoes employee prada outlet pay soccer jerseys to true religion outlet $70,000 sac louis vuitton AFP He's giuseppe zanotti already michael kors gained longchamp new louis vuitton outlet customers, air force too. "We've pandora charms definitely coach outlet store online gained p90x a new balance shoes handful air max of gucci customers hollister clothing store in beats by dre the louis vuitton last true religion jeans day louis vuitton uk or nfl jerseys two," kate spade handbags said ray ban sunglasses Stefan polo ralph lauren Bennett, nike tn a sac guess customer ray ban relations hollister manager michael kors outlet at hogan outlet Gravity timberland boots Payments, thomas sabo a air max credit north face card pandora charms payment ghd processing air max firm. "We're bottega veneta showing oakley sunglasses cheap people lancel you north face jackets can ray ban sunglasses run louis vuitton a polo ralph lauren outlet good longchamp company, ralph lauren and air max you gucci outlet can michael kors outlet pay longchamp handbags people air max fairly, hollister and nike free pas cher it mac cosmetics can new balance pas cher be vans pas cher profitable." Dan Price, christian louboutin chief insanity workout executive ralph lauren of michael kors outlet the nike roshe run company, air max pas cher stunned pandora jewelry his michael kors outlet online sale 100-plus kate spade outlet workers michael kors outlet on rolex watches Monday baseball bats when louboutin he north face outlet told hollister them abercrombie and fitch he nike huarache was swarovski crystal cutting longchamp handbags his coach purses roughly supra shoes$1 louis vuitton handbags million tiffany and co salary karen millen to chanel handbags \$70,000 timberland and true religion outlet using mcm handbags company air max profits asics running shoes to ray ban pas cher ensure oakley that sac louis vuitton everyone coach outlet there ferragamo shoes would celine handbags earn michael kors outlet at least michael kors that much sac longchamp within longchamp outlet three juicy couture outlet years.

For juicy couture outlet some mulberry workers, tiffany and co the jimmy choo outlet increase iphone 6 cases will louis vuitton outlet stores more polo ralph lauren th

Related burberry outlet Stories

Correction: nike free Seattle louis vuitton outlet Minimum nike air max Wage burberry outlet story polo lacoste Associated louboutin outlet Press

true religion jeans

Note: GameDev.net moderates article comments.