天天看點

Playing Nice Animations on XNA

FW: http://www.ziggyware.com/readarticle.php?article_id=190

Playing Nice Animations on XNA

by

Bruno Evangelista

Animations play an important role in games, whether your game is a First Person Shooter (FPS), a Puzzle or a Racing game you definitely want to have some animated objects on it. A character walking, an aircraft flying, a windmill rotating, a ball rolling, all these are example of animated models. On XNA, animated models have been a topic of much discussion on foruns and that is probably due to the fact that XNA only partially handle animated models (we will discuss this with more details on Section 5). In this article we will discuss about how models are handled on XNA, differences between rigid body and non-rigid body animations, skeletal animations, morphing, and finally, about how to play nice animations on XNA using the XNAnimation library.

1. Models on XNA

The XNA Game Studio has a Content Pipeline that allows you build assets from its original format to a new format that your game can load at runtime on Windows and Xbox 360. By default, the content pipeline can handle many different assets, such as models, textures, effects, and it is composed of many layers, where the four most important are: importers (convert the original file format to a default DOM), processors (process the asset in any way you like), compilers (generate a managed binary object that is stored) and loader (load the managed binary at runtime).

Playing Nice Animations on XNA

Models on XNA are represented by the Model class, which contains the data of an entire scene exported from a Digital Content Creation (DCC) tool. In other words, a Model might have many objects (or meshes) inside of it, where each mesh may have one or more materials. Each mesh in a model is represented by the ModelMesh class, where each part of the mesh that has a unique material is represented by the ModelMeshPart class. Each ModelMeshPart stores its material inside of an effect, which is represented by the Effect class. On XNA you must use effects to draw any object, therefore, when the Cotent Pipeline process a model that does not has an effect, it automatically creates and attach a XNA's BasicEffect to it. Lastly, the Model class stores the transformation (translation, rotation and scale) of each ModelMesh. Now that you understand how models are handled on XNA, let's start learning about animations and how to handle animated models.

2. Rigid Body and Non-Rigid Body Animations

In a racing game, a car is an animated model, because it moves over a racing track. The car's wheels are also animated models, because they rotate as the car moves. These types of animation are easily to be reproduced, for example, you can animate the car's wheels by just rotating it over its axis. Similarly, you can animate the car by moving (or translating) it over the track. You can notice that both animations have one thing in common, they do not affect or modify any of the car's meshes. Therefore, you can achieve this kind of animation by applying Rigid Body transformations (e.g. translations and rotations) over its meshes.

Playing Nice Animations on XNA

On XNA, each mesh in a model is handled separated, and you can maniputale each mesh's transformation through the Bones property of its parent Model. Look at the next example:

// Load my nice car model
Model model = Content.Load<Model>("MyCar");

// Get the index of the model's mesh "wheel1" transformation
int wheelIndex = model.Meshes["wheel1"].ParentBone.Index;

// Changes "wheel1" transformation
model.Bones[wheelIndex].Transform = Matrix.CreateRotationX(0.1f);

      

On the previous example, you loaded a car model and changed the transformation of one of its meshes, named "wheel1". Now, you just need to make sure that each mesh in the model is rendered using its

transformation. As show in the next code:

// Copy the absolute transformation of each 
// model's mesh to an array of matrices
Matrix[] absoluteTransformations = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transformations);

foreach (ModelMesh modelMesh in model.Meshes)
{
    foreach (BasicEffect effect in modelMesh.Effects) 
    {
        // Get the index of the transformation of this mesh
        int index = modelMesh.ParentBone.Index;

        // Set the mesh transformation as the 
        // World property in its effect
        effect.World = absoluteTransformations[index];
    }

    // Now you can draw the mesh!

    modelMesh.Draw();
}

      

Using this approach you can apply rigid body animations to any object that you want! Easy, isn't it? But and if you want to have a walking character in your game?

Playing Nice Animations on XNA

Looking at the walking character figure, you can notice that a character walking animation does modify the character's mesh, where the character mesh is deformed in each animation frame. In other words, you cannot handle them with ordinary rigid body transformations. In the next sections I will present two approaches that you might consider for handling deformable animated models: Skeletal Animation and Morphing. Both skeletal animations and morphing present an efficient way to handle animations that deform the model's mesh, each one having its advantages and disadvantages.

3. Skeletal Animation

In skeletal animation, each model is composed by one or more meshes and a skeleton. The model’s skeleton is a hierarchy of bones in a n-tree like form, and every vertex in each of the model’s meshes are linked to one or more skeleton’s bones. In this way, any time a bone is rotated, translated or even scaled the model's mesh will be deformed. For example, if you rotate a character’s forearm bone the forearm of the model’s mesh will be rotate too.

Playing Nice Animations on XNA

The previous figure shows an example of a skeleton in its original pose, and after one of its bones has being modified.

4. Morphing

In morphing, an animation is stored as an array of meshes containing different poses of a base mesh. Morphing has the advantage of modifying each vertex independently, and is usually faster to compute than skeletal animations. Because of that morphing is widely used for facial animations (specially in close-up views), while skeletal animations is used for general animations, such as walking, running, jumping, etc. Morphing animations tend to present a high memory footprint because it stores the entire mesh for each animation frame (although there are techniques that only stores the different between meshes, they still consumes a lot of memory).

In this article we are going to focus on skeletal animations, because it might provide a more efficient way to handle general character's animations. In the next section we are going to show how to handle skeletal animations on XNA.

5. Skeletal Animations on XNA

The XNA Content Pipeline partially supports skeletal animated models. But what that mean? That mean that it doesn't handle skeletal animations right out-of-the-box, but provides base functionalities that you can easily extend to do that.

We showed on Section 1 that the Content Pipeline include importers, processors and compilers. The default model importers, which handle FBX and X files, are capable of importing the meshes, skeleton, animations and materials of a model. After that, the default model processor partially handles the imported content, outputing the mode's meshes, skeleton, materials but no animations. That's why the Content Pipeline only partially supports animations.

If you want to handle animated models you will need to create your own model processor (which may extend the default one), capable of processing and outputting all the animations that you want. You also need to create an animation player, for playing the animations at runtime. Well, I can say it might be a lot of work for you. Fortunately, the XNA team already made a sample that shows how to handle Skeletal Animations, the

Skinned Model

sample. If you wanna try something more sophisticated than that, you can check some of the many XNA animation libraries available:

    • XNAnimation
    • KiloWatt Animation
    • Animation Component

6. Handling animations with XNAnimation

XNAnimation is a skeletal animation library that allows developers to easly manipulate, playback, interpolate and blend animations. In this section you will learn how to use XNAnimation to play animations.

6.1. Settings up Project

Go to

http://www.codeplex.com/xnanimation

and download the latest release of the XNAnimation library, extract it anywhere you want. Next, start you XNA Game Studio, and create a new Windows Game project. After creating your project, you need to add references for the XNAnimation library because you want to use its model processor and animation player.

Playing Nice Animations on XNA

In the Solution Explorer window, inside of your game project, right click on the References item and chose "Add References...". from the XNAnimation/Windows folder (from where you extracted the XNAnimation ZIP) and add it. Now, you are able to use any of the XNAnimation runtime classes but you still need to reference its content processor. To do that, in the Solution Explorer window, open the Content project inside of yours game project, right click on the References item and chose "Add References...". XNAnimationPipeline.DLL from the same folder of the XNAnimation.DLL and add it. You now have all the references that you need.

6.2. Setting the XNAnimation Model Processor

The XNAnimation runtime classes only handle models that were previously processed by its own model processor. In other words, if you let your models be processed by the default model processor the XNAnimation cannot handle them at runtime. So, start adding a skeletal animated model to your Content project. If you don't have an animated model, you can add any of the sample models that come with XNAnimation. Just browse a model from the "XNAnimationSamples/Samples Content/Content/Models" folder, and its textures from the "XNAnimation Samples/Samples Content/Content/Textures" folder. Next, right click on the model and select "Properties" to open the Properties window.

In the Properties window, select the Content Processor item and change its value to "Model - XNAnimation" (that is the XNAnimation model processor). If you expand the Content Processor item, you will see that it has a lot of parameters, such as "Dump Animations", "Generate Tangent Frame", "Texture Path" and many others. If you have your animated models and textures at the same folder inside the Content project (which is true if you had added one of the sample models and textures), you must change the "Texture Path" value to "./". In this case, the model processor will search for the model's textures at the same folder. Now, select Build (or just press F5) and see in the Output Window your model being processed by the Content Pipeline.

Playing Nice Animations on XNA

6.3. Playing Animations

At this point your model was already processed by the XNAnimation model processor and is ready to be loaded . In this section you will load your animated model and play its animation. First, go to the beginning of your Game class and declare the namespaces you will use:

using XNAnimation;
using XNAnimation.Controllers;
using XNAnimation.Effects;

      

Next, in the beginning of your Game class (outside of any the method) declare a SkinnedModel and an AnimationController. You will use the SkinnedModel to store your animated model (which contains its skeleton and animations), and the AnimationController to control the animation playback:

SkinnedModel skinnedModel;
AnimationController animationController;

      

Then, go to the Load method of your Game class, load the animated model that was previously processed and create an animation controller for it.

protected override void LoadContent()
{
    skinnedModel = Content.Load<SkinnedModel>("ModelNameWithoutExtension");

    animationController = new AnimationController(skinnedModel.SkeletonBones);
}

      

The animated model is loaded through the content manager. In the code above you must change the ModelNameWithoutExtension, with the name of your animated model. The AnimationController is created passing an skeleton for its constructor, in this case you should pass the skeleton of the model that you just loaded.

You will handle the model's animations through the AnimationController that you created, which allows you to start a new animation clip, control its speed, looping, forward/backward playback and more. You will use the StartClip method of the AnimationController to start a new animation clip, as shown next:

animationController.StartClip(skinnedModel.AnimationClips["Idle"]);

      

The StartClip method receives as its parameter the animation clip to be started. You can access the animated model animations through the AnimationClips property of the SkinnedModel class, and you can query the animation clips by their name or index. After starting an animation clip, you need to keep updating the AnimationController constantly. You can do that calling the Update method of the AnimationController inside the Update method of your Game class:

protected override void Update(GameTime gameTime)
{
    animationController.Update(gameTime.ElapsedGameTime, Matrix.Identity);
    base.Update(gameTime);
}

      

The Update method of the AnimationController receives as its parameters the elapsed time (since the last update) and an optional "parent tranformation". The elapsed time is used to advance the animation that is being played, and the parent transformation to attach one animated object in another. For example, you can use it to attach an animated whip to the player's hand. Finally, you can draw your animated model like any other XNA Model, using the following code:

protected override void Draw(GameTime gameTime)
{
    graphics.GraphicsDevice.Clear(Color.Gray);

    foreach (ModelMesh modelMesh in skinnedModel.Model.Meshes)
    {
        foreach (SkinnedModelBasicEffect effect in modelMesh.Effects)
        {
            // Set the animated skeleton from the animation controller
            effect.Bones = animationController.SkinnedBoneTransforms;

            // Set the camera 
            effect.View = Matrix.CreateLookAt(
                            new Vector3(40), Vector3.Zero, Vector3.Up);

            effect.Projection = 
                    Matrix.CreatePerspectiveFieldOfView(1, 1, 1, 10000);

            // OPTIONAL - Configure material and light
            effect.Material.DiffuseColor = new Vector3(0.8f);
            effect.AmbientLightColor = new Vector3(0.2f);
            effect.LightEnabled = true;
            effect.EnabledLights = EnabledLights.Two;
            effect.PointLights[0].Color = Vector3.One;
            effect.PointLights[0].Position = new Vector3(100);
        }
        modelMesh.Draw();
    }
    base.Draw(gameTime);
}

      
Playing Nice Animations on XNA

7. Conclusions

Animations play a major role on many games and you definitely want to use them. The XNAnimation presents a very simple and flexible way to handle skeletal animations on XNA, it is open source and you can use it in commercial or non-commercial applications.

繼續閱讀