#region File Description
//-----------------------------------------------------------------------------
// CustomAvatarAnimationSample.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using CustomAvatarAnimation;
#endregion
namespace CustomAvatarAnimationSample
{
///
/// The possible animation types on the avatar.
///
public enum AnimationType { Idle1, Idle2, Idle3, Idle4, Walk,
Jump, Kick, Punch, Faint };
///
/// This is the main type for your game
///
public class CustomAvatarAnimationSampleGame : Microsoft.Xna.Framework.Game
{
#region Graphics Data
GraphicsDeviceManager graphics;
// Model to display the ground
Model groundModel;
// World, View and Projection matricies used for rendering
Matrix world;
Matrix view;
Matrix projection;
// the following constants control the camera's default position
const float CameraDefaultArc = MathHelper.Pi / 10;
const float CameraDefaultRotation = MathHelper.Pi;
const float CameraDefaultDistance = 2.5f;
// Camera values
float cameraArc = CameraDefaultArc;
float cameraRotation = CameraDefaultRotation;
float cameraDistance = CameraDefaultDistance;
#endregion
#region Avatar Data
// The AvatarDescription and AvatarRenderer that are used to render the avatar
AvatarRenderer avatarRenderer;
AvatarDescription avatarDescription;
// Animations that will be used
IAvatarAnimation[] animations;
// Tells us if we are using an idle, walking, or other animations
AnimationType currentType;
#endregion
#region Fields
// Store the current and last gamepad state
GamePadState currentGamePadState;
GamePadState lastGamePadState;
// A random number generator for picking new idle animations
Random random = new Random();
#endregion
#region Initialization
///
/// Creates a new AvatarCustomAnimationSample object.
///
public CustomAvatarAnimationSampleGame()
{
// initialize the graphics
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
graphics.PreferMultiSampling = true;
// initialize gamer services
Components.Add(new GamerServicesComponent(this));
SignedInGamer.SignedIn += new EventHandler(SignedInGamer_SignedIn);
}
///
/// Handle signed in gamer event as start avatar loading
///
void SignedInGamer_SignedIn(object sender, SignedInEventArgs e)
{
// Only load the avatar for player one
if (e.Gamer.PlayerIndex == PlayerIndex.One)
{
// Load the player one avatar
LoadAvatar(e.Gamer);
}
}
///
/// Load all graphical content.
///
protected override void LoadContent()
{
// Load custom animations
CustomAvatarAnimationData animationData;
// We will use 8 different animations
animations = new IAvatarAnimation[9];
// Load the idle animations
for (int i = 0; i < 4; i++)
{
animations[i] = new AvatarAnimation(
(AvatarAnimationPreset)((int)AvatarAnimationPreset.Stand0 + i));
}
// Load the walk animation
animationData = Content.Load("Walk");
animations[4] = new CustomAvatarAnimationPlayer(animationData.Name, animationData.Length,
animationData.Keyframes, animationData.ExpressionKeyframes);
// Load the jump animation
animationData = Content.Load("Jump");
animations[5] = new CustomAvatarAnimationPlayer(animationData.Name, animationData.Length,
animationData.Keyframes, animationData.ExpressionKeyframes);
// Load the kick animation
animationData = Content.Load("Kick");
animations[6] = new CustomAvatarAnimationPlayer(animationData.Name, animationData.Length,
animationData.Keyframes, animationData.ExpressionKeyframes);
// Load the punch animation
animationData = Content.Load("Punch");
animations[7] = new CustomAvatarAnimationPlayer(animationData.Name, animationData.Length,
animationData.Keyframes, animationData.ExpressionKeyframes);
// Load the faint animation
animationData = Content.Load("Faint");
animations[8] = new CustomAvatarAnimationPlayer(animationData.Name, animationData.Length,
animationData.Keyframes, animationData.ExpressionKeyframes);
// Load the model for the ground
groundModel = Content.Load("ground");
// Select a random idle animation to start
PlayRandomIdle();
// Create the projection to use
projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
GraphicsDevice.Viewport.AspectRatio, .01f, 200.0f);
world = Matrix.Identity;
}
#endregion
#region Updating
protected override void Update(GameTime gameTime)
{
// Get the current gamepad state and store the old
lastGamePadState = currentGamePadState;
currentGamePadState = GamePad.GetState(PlayerIndex.One);
// Allows the game to exit
if (currentGamePadState.Buttons.Back == ButtonState.Pressed)
this.Exit();
// Handle gamer input
HandleAvatarInput(gameTime);
HandleCameraInput();
// Loop animation if we are walking
bool loopAnimation = (currentType == AnimationType.Walk);
// Update the current animation
animations[(int)currentType].Update(gameTime.ElapsedGameTime, loopAnimation);
// Check to see if we need to end the animation
if (!loopAnimation)
{
if (animations[(int)currentType].CurrentPosition ==
animations[(int)currentType].Length)
{
// Start new idle animation
PlayRandomIdle();
}
}
base.Update(gameTime);
}
#endregion
#region Drawing
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// Draw the ground first since the avatar has transparent parts
groundModel.Draw(Matrix.Identity, view, projection);
// Draw the avatar
if (avatarRenderer != null)
{
avatarRenderer.Draw(animations[(int)currentType]);
}
base.Draw(gameTime);
}
#endregion
#region Input Handling
///
/// Check for user input to play animations on the avatar
///
private void HandleAvatarInput(GameTime gameTime)
{
// Check to see if the user wants to load a random avatar
// by pressing the Right Shoulder
if (currentGamePadState.Buttons.RightShoulder == ButtonState.Pressed &&
lastGamePadState.Buttons.RightShoulder != ButtonState.Pressed)
{
LoadRandomAvatar();
}
// Check to see if the user wants to load the users avatar
// by pressing the Left Shoulder
else if (currentGamePadState.Buttons.LeftShoulder == ButtonState.Pressed &&
lastGamePadState.Buttons.LeftShoulder != ButtonState.Pressed)
{
// Load the users avatar
if (SignedInGamer.SignedInGamers[PlayerIndex.One] != null)
{
LoadAvatar(SignedInGamer.SignedInGamers[PlayerIndex.One]);
}
}
// Check to see if the user wants to play one of the other
// animations by pressing one of the gamepad buttons
if (currentGamePadState.Buttons.A == ButtonState.Pressed &&
lastGamePadState.Buttons.A != ButtonState.Pressed)
{
PlayAnimation(AnimationType.Jump);
}
if (currentGamePadState.Buttons.B == ButtonState.Pressed &&
lastGamePadState.Buttons.B != ButtonState.Pressed)
{
PlayAnimation(AnimationType.Kick);
}
if (currentGamePadState.Buttons.X == ButtonState.Pressed &&
lastGamePadState.Buttons.X != ButtonState.Pressed)
{
PlayAnimation(AnimationType.Punch);
}
if (currentGamePadState.Buttons.Y == ButtonState.Pressed &&
lastGamePadState.Buttons.Y != ButtonState.Pressed)
{
PlayAnimation(AnimationType.Faint);
}
// Update the avatars location
UpdateAvatarMovement(gameTime);
}
///
/// Update the avatars movement based on user input
///
private void UpdateAvatarMovement(GameTime gameTime)
{
// Create vector from the left thumbstick location
Vector2 leftThumbStick = currentGamePadState.ThumbSticks.Left;
// The direction for our Avatar
Vector3 avatarForward = world.Forward;
// The amount we want to translate
Vector3 translate = Vector3.Zero;
// Clamp thumbstick to make sure the user really wants to move
if (leftThumbStick.Length() > 0.2f)
{
// Create our direction vector
leftThumbStick.Normalize();
// Find the new avatar forward
avatarForward.X = leftThumbStick.X;
avatarForward.Y = 0;
avatarForward.Z = -leftThumbStick.Y;
// Translate the thumbstick using the current camera rotation
avatarForward = Vector3.Transform(avatarForward,
Matrix.CreateRotationY(cameraRotation));
avatarForward.Normalize();
// Determine the amount of translation
translate = avatarForward
* ((float)gameTime.ElapsedGameTime.TotalMilliseconds
* 0.0009f);
// We are now walking
currentType = AnimationType.Walk;
}
else
{
// If we were walking last frame pick a random idle animation
if (currentType == AnimationType.Walk)
{
PlayRandomIdle();
}
}
// Update the world matrix
world.Forward = avatarForward;
// Normalize the matrix
world.Right = Vector3.Cross(world.Forward, Vector3.Up);
world.Right = Vector3.Normalize(world.Right);
world.Up = Vector3.Cross(world.Right, world.Forward);
world.Up = Vector3.Normalize(world.Up);
// Add translation
world.Translation += translate;
// Set the avatar renderer world matrix
if (avatarRenderer != null)
{
avatarRenderer.World = world;
}
}
///
/// Move camera based on user input
///
private void HandleCameraInput()
{
// should we reset the camera?
if (currentGamePadState.Buttons.RightStick == ButtonState.Pressed)
{
cameraArc = CameraDefaultArc;
cameraDistance = CameraDefaultDistance;
cameraRotation = CameraDefaultRotation;
}
// Update Camera
cameraArc -= currentGamePadState.ThumbSticks.Right.Y * 0.05f;
cameraRotation += currentGamePadState.ThumbSticks.Right.X * 0.1f;
cameraDistance += currentGamePadState.Triggers.Left * 0.1f;
cameraDistance -= currentGamePadState.Triggers.Right * 0.1f;
// Limit the camera movement
if (cameraDistance > 5.0f)
cameraDistance = 5.0f;
else if (cameraDistance < 2.0f)
cameraDistance = 2.0f;
if (cameraArc > MathHelper.Pi / 5)
cameraArc = MathHelper.Pi / 5;
else if (cameraArc < -(MathHelper.Pi / 5))
cameraArc = -(MathHelper.Pi / 5);
// Update the camera position
Vector3 cameraPos = new Vector3(0, cameraDistance, cameraDistance);
cameraPos = Vector3.Transform(cameraPos, Matrix.CreateRotationX(cameraArc));
cameraPos = Vector3.Transform(cameraPos,
Matrix.CreateRotationY(cameraRotation));
cameraPos += world.Translation;
// Create new view matrix
view = Matrix.CreateLookAt(cameraPos, world.Translation
+ new Vector3(0, 1.2f, 0), Vector3.Up);
// Set the new view on the avatar renderer
if (avatarRenderer != null)
{
avatarRenderer.View = view;
}
}
#endregion
#region Animation Selection
///
/// Start playing a random idle animation
///
private void PlayRandomIdle()
{
PlayAnimation((AnimationType)random.Next((int)AnimationType.Idle4));
}
///
/// Start playing one of the other animations that were loaded
///
private void PlayAnimation(AnimationType animation)
{
animations[(int)animation].CurrentPosition = TimeSpan.Zero;
currentType = animation;
}
#endregion
#region Avatar Loading
///
/// Load the avatar for a gamer
///
private void LoadAvatar(Gamer gamer)
{
UnloadAvatar();
AvatarDescription.BeginGetFromGamer(gamer, LoadAvatarDescription, null);
}
///
/// AsyncCallback for loading the AvatarDescription
///
private void LoadAvatarDescription(IAsyncResult result)
{
// Get the AvatarDescription for the gamer
avatarDescription = AvatarDescription.EndGetFromGamer(result);
// Load the AvatarRenderer if description is valid
if (avatarDescription.IsValid)
{
avatarRenderer = new AvatarRenderer(avatarDescription);
avatarRenderer.Projection = projection;
}
// Load random for an invalid description
else
{
LoadRandomAvatar();
}
}
///
/// Load a random avatar
///
private void LoadRandomAvatar()
{
UnloadAvatar();
avatarDescription = AvatarDescription.CreateRandom();
avatarRenderer = new AvatarRenderer(avatarDescription);
avatarRenderer.Projection = projection;
}
///
/// Unloads the current avatar
///
private void UnloadAvatar()
{
// Dispose the current Avatar
if (avatarRenderer != null)
{
avatarRenderer.Dispose();
avatarRenderer = null;
}
}
#endregion
#region Entry Point
///
/// The main entry point for the application.
///
static void Main(string[] args)
{
using (CustomAvatarAnimationSampleGame game =
new CustomAvatarAnimationSampleGame())
{
game.Run();
}
}
#endregion
}
}