6 min read

Basic Animation

Introduction

In a 3D game, one of the big things we want to do is be able to have our game objects move around, which is called animation. This is a simple tutorial that discusses the basics of 3D animation in your XNA game. For this tutorial, we will start with where we left off in the introductory tutorial on using 3D models It might be a good idea to get the code from the end of that tutorial to use as a starting point for this one. In this short tutorial, we will take a look at how animation is done in XNA, in its simplest form. There are other more advanced techniques that we will discuss in future tutorials.

Simple 3D Animation

In the real world, we think about moving objects as a continuous process that is gradually happening across a span of time. In video games, like in movies and cartoons, animation does not occur continuously, but rather in discrete time intervals. By default, an XNA game will be redrawn 30 times a second. You can actually configure this to be different in XNA, but that is a different topic for another tutorial. This is actually a good thing because otherwise, you would need to know Calculus in order to make a game! Instead, 30 times a second, we will be given an opportunity to update the state of our game, including the positions of our objects, and then we can draw the models in the current position each time. By doing this, an object will be able to move around in our game world.

Create a Place to Store the Position

The first thing we will want to do is to create something to store the position of our model in our 3D world. We will use a Vector3 object. Remember that a vector is simply a list of numbers. In this case, there are three numbers, which represent the x-, y-, and z-coordinates of the position of our object. So let’s add the following line of code as an instance variable to our game class:

private Vector3 position;

Initialize the Position

Next, let’s initialize the position. Depending on what your game does, and how you’ve got it set up, you can initialize the position anywhere, but for now, let’s go to the LoadContent() method and put this line of code, to start our object’s position at the origin:

position= new Vector3(0, 0, 0);

Update the Position Each Frame

The next thing we will need to do is to update the position. In your game, the Update() method is called 30 times a second by default. You can actually set it up so that the Update() method and the Draw() method are not called the same number of times per second, so it is important that you keep updating code in the Update() method and drawing code in the Draw() method. Updating this position is pretty simple. Add the following line of code somewhere in the Update() method:

position += new Vector3(0, 0.01f, 0);

This will move the object 0.01 units in the y-direction every update.

The last thing we will need to do is update the world matrix that we’ve been using for drawing. Do this by adding the following line of code in your Update() method, just after you change the position:

world = Matrix.CreateTranslation(position);

You should now be able to run your game again, and see the object move across the screen, like in the screenshot below.

Screenshot 1

A More Advanced Animation

Before we finish this tutorial, I thought it would be worth doing a slightly more complicated animation. For this animation, we are going to have our object spin around while it moves. To do this, we will need to keep track of an angle of rotation, so go back to your instance variables and add the following line:

private float angle;

We can initialize the angle to 0 radians in the LoadContent() method like we did with the position variable:

angle = 0;

Next, we will need to update the angle, just like we did with the position, so go back to your Update() method and add the following line of code at some point before you create the new world matrix:

angle += 0.03f;

Finally, we need to make our world matrix incorporate both the angle and the position into it, so change the line that says:

world = Matrix.CreateTranslation(position);

to say:

world = Matrix.CreateRotationY(angle) * Matrix.CreateTranslation(position);

You should now be able to run the game again, and you should see the object spinning as it moves along.

Screenshot 2

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Using3DModels
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        /// <summary>
        /// Stores the model that we are going to draw.
        /// </summary>
        private Model model;

        /// <summary>
        /// Stores the position of the model.
        /// </summary>
        private Vector3 position;

        /// <summary>
        /// Stores the rotation angle of the model.  This
        /// is used as a roll angle. (So changing it will make the
        /// object roll as it moves forward.)
        /// </summary>
        private float angle;

        /// <summary>
        /// Stores the world matrix for the model, which transforms the
        /// model to be in the correct position, scale, and rotation
        /// in the game world.
        /// </summary>
        private Matrix world = Matrix.CreateTranslation(new Vector3(0, 0, 0));

        /// <summary>
        /// Stores the view matrix for the model, which gets the model
        /// in the right place, relative to the camera.
        /// </summary>
        private Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, 10), new Vector3(0, 0, 0), Vector3.UnitY);

        /// <summary>
        /// Stores the projection matrix, which gets the model projected
        /// onto the screen in the correct way.  Essentially, this defines the
        /// properties of the camera you are using.
        /// </summary>
        private Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), 800f / 480f, 0.1f, 100f);


        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            model = Content.Load<Model>("Ship");
            position = new Vector3(0, 0, 0);
            angle = 0;
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // Update the properties of the model here
            position += new Vector3(0, 0.01f, 0);
            angle += 0.03f;

            // This updates the world matrix, so that it reflects the changes to the position
            // and angle.  Remember that this matrix determines where the model will be located
            // at in the 3D world.
            world = Matrix.CreateRotationY(angle) * Matrix.CreateTranslation(position);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            DrawModel(model, world, view, projection);

            base.Draw(gameTime);
        }

        /// <summary>
        /// Does the work of drawing a model, given specific world, view, and projection
        /// matrices.
        /// </summary>
        /// <param name="model">The model to draw</param>
        /// <param name="world">The transformation matrix to get the model in the right place in the world.</param>
        /// <param name="view">The transformation matrix to get the model in the right place, relative to the camera.</param>
        /// <param name="projection">The transformation matrix to project the model's points onto the screen correctly.</param>
        private void DrawModel(Model model, Matrix world, Matrix view, Matrix projection)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.World = world;
                    effect.View = view;
                    effect.Projection = projection;
                }

                mesh.Draw();
            }
        }

    }
}
Download the completed project