#region File Description
//-----------------------------------------------------------------------------
// AvatarMultipleAnimationsSample.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion
namespace AvatarMultipleAnimationsSample
{
///
/// Defines what animation the avatar should be using
///
public enum AnimationPlaybackMode { All, Celebrate, Wave };
///
/// This is the main type for your game
///
public class AvatarMultipleAnimationsGame : Microsoft.Xna.Framework.Game
{
#region Fields
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont font;
// Description, renderer, and animation for
// loading and drawing an animated avatar
AvatarDescription avatarDescription;
AvatarRenderer avatarRenderer;
AvatarAnimation waveAnimation;
AvatarAnimation celebrateAnimation;
// List of the final list of bone transforms.
// The list will contain both transforms from the wave and clap animations.
List finalBoneTransforms = new List(AvatarRenderer.BoneCount);
// List of the bone index values for the right arm and its children
List rightArmBones;
// Playback mode defines what animations should be playing
AnimationPlaybackMode animationPlaybackMode;
// matrices used to draw the avatar
Matrix world;
Matrix view;
Matrix projection;
// The current input states. These are updated in the HandleInput function,
// and used primarily in the UpdateCamera function.
GamePadState currentGamePadState;
GamePadState lastGamePadState;
// the following constants control the speed at which the camera moves
// how fast does the camera move up, down, left, and right?
const float CameraRotateSpeed = .1f;
// how fast does the camera zoom in and out?
const float CameraZoomSpeed = .01f;
// the camera can't be further away than this distance
const float CameraMaxDistance = 10.0f;
// and it can't be closer than this
const float CameraMinDistance = 2.0f;
// the following constants control the camera's default position
const float CameraDefaultArc = 30.0f;
const float CameraDefaultRotation = 0;
const float CameraDefaultDistance = 3.0f;
// Camera control values
float cameraArc = CameraDefaultArc;
float cameraRotation = CameraDefaultRotation;
float cameraDistance = CameraDefaultDistance;
#endregion
#region Initialization
///
/// Constructor.
///
public AvatarMultipleAnimationsGame()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
graphics.PreferMultiSampling = true;
// Avatars require GamerServices
Components.Add(new GamerServicesComponent(this));
}
///
/// Load your graphics content.
///
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load("Font");
// Create random avatar description and load the renderer and animation
avatarDescription = AvatarDescription.CreateRandom();
avatarRenderer = new AvatarRenderer(avatarDescription);
// Load the preset animations
waveAnimation = new AvatarAnimation(AvatarAnimationPreset.Wave);
celebrateAnimation = new AvatarAnimation(AvatarAnimationPreset.Celebrate);
// Find the bone index values for the right arm and its children
rightArmBones = FindInfluencedBones(AvatarBone.ShoulderRight,
avatarRenderer.ParentBones);
for (int i = 0; i < AvatarRenderer.BoneCount; ++i)
{
finalBoneTransforms.Add(Matrix.Identity);
}
// Initialize the rendering matrices
world = Matrix.CreateRotationY(MathHelper.Pi);
projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
GraphicsDevice.Viewport.AspectRatio,
.01f, 200.0f);
}
#endregion
#region Update and Draw
///
/// Allows the game to run logic.
///
protected override void Update(GameTime gameTime)
{
HandleInput();
UpdateCamera(gameTime);
// Set avatar rendering matrices
avatarRenderer.World = world;
avatarRenderer.View = view;
avatarRenderer.Projection = projection;
// Update the current animation and world space bones
if (avatarRenderer.State == AvatarRendererState.Ready)
{
waveAnimation.Update(gameTime.ElapsedGameTime, true);
celebrateAnimation.Update(gameTime.ElapsedGameTime, true);
UpdateTransforms();
}
base.Update(gameTime);
}
///
/// This is called when the game should draw itself.
///
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
string message = "Playing: ";
if (animationPlaybackMode == AnimationPlaybackMode.All)
{
message += "Celebrate + Wave";
}
else if (animationPlaybackMode == AnimationPlaybackMode.Celebrate)
{
message += "Celebrate";
}
else if (animationPlaybackMode == AnimationPlaybackMode.Wave)
{
message += "Wave";
}
message += "\n(Left Shoulder Button)";
// Draw the text
spriteBatch.Begin();
spriteBatch.DrawString(font, message, new Vector2(161, 161), Color.Black);
spriteBatch.DrawString(font, message, new Vector2(160, 160), Color.White);
spriteBatch.End();
// Draw the avatar with the combined transforms
avatarRenderer.Draw(finalBoneTransforms, celebrateAnimation.Expression);
base.Draw(gameTime);
}
///
/// Combines the transforms of the clap and wave animations
///
private void UpdateTransforms()
{
// List of bone transforms from the clap and wave animations
ReadOnlyCollection celebrateTransforms =
celebrateAnimation.BoneTransforms;
ReadOnlyCollection waveTransforms = waveAnimation.BoneTransforms;
// Check to see if we are playing both of the animations
if (animationPlaybackMode == AnimationPlaybackMode.All)
{
// Copy the celebrate transforms to the final list of transforms
for (int i = 0; i < finalBoneTransforms.Count; i++)
{
finalBoneTransforms[i] = celebrateTransforms[i];
}
// Overwrite the transforms for the bones that are part of the right arm
// with the wave animation transforms.
for (int i = 0; i < rightArmBones.Count; i++)
{
finalBoneTransforms[rightArmBones[i]] =
waveTransforms[rightArmBones[i]];
}
}
// Check to see if we are just playing the celebrate animation
else if (animationPlaybackMode == AnimationPlaybackMode.Celebrate)
{
// Copy the celebrate transforms to the final list of transforms
for (int i = 0; i < finalBoneTransforms.Count; i++)
{
finalBoneTransforms[i] = celebrateTransforms[i];
}
}
else if (animationPlaybackMode == AnimationPlaybackMode.Wave)
{
// We are just using the wave so use all bones
for (int i = 0; i < finalBoneTransforms.Count; i++)
{
finalBoneTransforms[i] = waveTransforms[i];
}
}
}
///
/// Creates a list of bone index values for the given avatar bone
/// and its children.
///
/// The root bone to start search
/// List of parent bones from the avatar
/// renderer
///
List FindInfluencedBones(AvatarBone avatarBone,
ReadOnlyCollection parentBones)
{
// New list of bones that will be influenced
List influencedList = new List();
// Add the first bone
influencedList.Add((int)avatarBone);
// Start searching after the first bone
int currentBoneID = influencedList[0] + 1;
// Loop until we are done with all of the bones
while (currentBoneID < parentBones.Count)
{
// Check to see if the current bone is a child of any of the
// previous bones we have found
if (influencedList.Contains(parentBones[currentBoneID]))
{
// Add the bone to the influenced list
influencedList.Add(currentBoneID);
}
// Move to the next bone
currentBoneID++;
}
return influencedList;
}
#endregion
#region Input and Camera
///
/// Handles input for quitting the game.
///
void HandleInput()
{
lastGamePadState = currentGamePadState;
currentGamePadState = GamePad.GetState(PlayerIndex.One);
// Check for exit.
if (currentGamePadState.Buttons.Back == ButtonState.Pressed)
{
Exit();
}
// Check to see if we need to change play modes
if (currentGamePadState.Buttons.LeftShoulder == ButtonState.Pressed &&
lastGamePadState.Buttons.LeftShoulder != ButtonState.Pressed)
{
animationPlaybackMode += 1;
if (animationPlaybackMode > AnimationPlaybackMode.Wave)
{
animationPlaybackMode = AnimationPlaybackMode.All;
}
}
// Check to see if we should load another random avatar
if (currentGamePadState.Buttons.RightShoulder == ButtonState.Pressed &&
lastGamePadState.Buttons.RightShoulder != ButtonState.Pressed)
{
avatarDescription = AvatarDescription.CreateRandom();
avatarRenderer = new AvatarRenderer(avatarDescription);
}
}
///
/// Handles input for moving the camera.
///
void UpdateCamera(GameTime gameTime)
{
float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
// should we reset the camera?
if (currentGamePadState.Buttons.RightStick == ButtonState.Pressed)
{
cameraArc = CameraDefaultArc;
cameraDistance = CameraDefaultDistance;
cameraRotation = CameraDefaultRotation;
}
// Check for input to rotate the camera up and down around the model.
cameraArc += currentGamePadState.ThumbSticks.Right.Y * time *
CameraRotateSpeed;
// Limit the arc movement.
cameraArc = MathHelper.Clamp(cameraArc, -90.0f, 90.0f);
// Check for input to rotate the camera around the model.
cameraRotation += currentGamePadState.ThumbSticks.Right.X * time *
CameraRotateSpeed;
// Check for input to zoom camera in and out.
cameraDistance += currentGamePadState.Triggers.Left * time
* CameraZoomSpeed;
cameraDistance -= currentGamePadState.Triggers.Right * time
* CameraZoomSpeed;
// clamp the camera distance so it doesn't get too close or too far away.
cameraDistance = MathHelper.Clamp(cameraDistance,
CameraMinDistance, CameraMaxDistance);
Matrix unrotatedView = Matrix.CreateLookAt(
new Vector3(0, 0, cameraDistance), new Vector3(0, 1, 0), Vector3.Up);
view = Matrix.CreateRotationY(MathHelper.ToRadians(cameraRotation)) *
Matrix.CreateRotationX(MathHelper.ToRadians(cameraArc)) *
unrotatedView;
}
#endregion
}
#region Entry Point
///
/// The main entry point for the application.
///
static class Program
{
static void Main()
{
using (AvatarMultipleAnimationsGame game = new AvatarMultipleAnimationsGame())
{
game.Run();
}
}
}
#endregion
}