codesamples >> quantum animation class

Quantum: Animation system

Project:

Quantum

Engine:

XNA

Code language:

C#

Class description:

I've started out using the SkinnedModelProcessor provided by Microsoft and I've added some features to retrieve the exact time and information for each animation.
This allowed us to make sounds synchronised with the animations (e.g. when the character is walking each step will make a sound).
This has been done for each character in the game, I will use the Vigor class to demonstrate.

Vigor.cs - LoadContent

// ...            
/// 
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// 
protected override void LoadContent()
{
    // TODO: use this.Content to load your game content here
    m_StunEffectModel = base.Game.Content.Load("Models\\Characters\\Vigor\\vigor_stunEffect");

    // Animation loading for Vigor

    // Load the model.
    m_AnimatedModel[(int)Vigor.PoseState.Idle] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_Idle");
    m_AnimatedModel[(int)Vigor.PoseState.Run] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_Run");
    m_AnimatedModel[(int)Vigor.PoseState.Charge] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_Charge");
    m_AnimatedModel[(int)Vigor.PoseState.Release] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_Release");
    m_AnimatedModel[(int)Vigor.PoseState.Ability] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_AbilityFaster");
    m_AnimatedModel[(int)Vigor.PoseState.EnergyIdle] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_EnergyIdle");
    m_AnimatedModel[(int)Vigor.PoseState.Backwalk] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_Backwalk");
    m_AnimatedModel[(int)Vigor.PoseState.StrafingLeft] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_StrafingLeft");
    m_AnimatedModel[(int)Vigor.PoseState.StrafingRight] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_StrafingRight");
    m_AnimatedModel[(int)Vigor.PoseState.SprintForwards] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_FastRunAbility");
    m_AnimatedModel[(int)Vigor.PoseState.Dragging] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_DragWalk");
    m_AnimatedModel[(int)Vigor.PoseState.Deactivating] = base.Game.Content.
   Load("Models\\Characters\\Vigor\\VigorV2_ShutDown");

    // Look up our custom skinning information.
    SkinningData[] VigorSkinningDataArr = new SkinningData[m_AmountOfAnimatedVigorModels];

    //AnimationClip[] VigorClipArr = new AnimationClip[m_AmountOfAnimatedVigorModels];
    for (int index = 0; index < m_AmountOfAnimatedVigorModels; ++index)
    {
        VigorSkinningDataArr[index] = m_AnimatedModel[index].Tag as SkinningData;
        if (VigorSkinningDataArr[index] == null)
            throw new InvalidOperationException
                ("This model does not contain a SkinningData tag.");
        // Create an animation player, and start decoding an animation clip.
        m_AnimationPlayer[index] = new AnimationPlayer(VigorSkinningDataArr[index]);
        m_ClipArr[index] = VigorSkinningDataArr[index].AnimationClips["Take 001"];
        m_AnimationPlayer[index].StartClip(m_ClipArr[index]);
    }
}
// ...
            

Vigor.cs - Update

// ...
// Update AnimationTime
for (int index = 0; index < m_AmountOfAnimatedVigorModels; ++index)
{
    m_AnimationPlayer[index].Update(gameTime.ElapsedGameTime, true, Matrix.Identity);
}

TimeSpan smallTimeDifference = new TimeSpan(0, 0, 0, 0, 25);
TimeSpan startTime = new TimeSpan(0, 0, 0, 0, 0);

// Select active Model / Animation
switch (m_Pose)
{
    case ((int)Vigor.PoseState.Idle):
        if (CurrentEnergyLevel > 0)
        {
            m_Pose = (int)Vigor.PoseState.EnergyIdle;
        }
        else
        {
            m_ActiveModel = m_AnimatedModel[(int)Vigor.PoseState.Idle];
            m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.Idle];
        }
        break;

    case ((int)Vigor.PoseState.Run):
        TimeSpan leftFoot = new TimeSpan(0, 0, 0, 0, 0);
        TimeSpan rightFoot = new TimeSpan(0, 0, 0, 0, 500);
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Run] 
        && (m_ActiveAnimationPlayer.CurrentTime > leftFoot - smallTimeDifference 
        && m_ActiveAnimationPlayer.CurrentTime < leftFoot + smallTimeDifference))
        {
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorLeftFoot, false);
        }
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Run] 
        && (m_ActiveAnimationPlayer.CurrentTime > rightFoot - smallTimeDifference 
        && m_ActiveAnimationPlayer.CurrentTime < rightFoot + smallTimeDifference))
        {
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorRightFoot, false);
        }
        m_ActiveModel = m_AnimatedModel[(int)Vigor.PoseState.Run];
        m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.Run];
        break;

    case ((int)Vigor.PoseState.Charge):
        m_ActiveModel = m_AnimatedModel[(int)Vigor.PoseState.Charge];
        m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.Charge];
        break;

    case ((int)Vigor.PoseState.Release):
        m_ActiveModel = m_AnimatedModel[(int)Vigor.PoseState.Release];
        m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.Release];

        TimeSpan slightTimeDifference = new TimeSpan(0, 0, 0, 0, 400);
        TimeSpan loopTime = new TimeSpan(0, 0, 0, 0, 720);

        // Make him loop at the charging part
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Release] 
        && m_ActiveAnimationPlayer.CurrentTime > m_ClipArr[(int)Vigor.PoseState.Release].Duration - 
        slightTimeDifference 
        && CurrentEnergyLevel > 0)
        {
            m_ActiveAnimationPlayer.SetCurrentTime(loopTime, 18);
        }

        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Release] 
        && ActiveAnimationPlayer.CurrentTime > m_ClipArr[(int)Vigor.PoseState.Release].Duration - 
        smallTimeDifference)
        {
            m_Pose = (int)Vigor.PoseState.Idle;
        }

        break;

    case ((int)Vigor.PoseState.Ability):
        m_ActiveModel = m_AnimatedModel[(int)Vigor.PoseState.Ability];
        
        TimeSpan halfTimeDifference = new TimeSpan(0, 0, 0, 0, 350);

        // Start animation & lock state
        if (m_ActiveAnimationPlayer != m_AnimationPlayer[(int)Vigor.PoseState.Ability] 
        && m_ActiveAnimationPlayer.CurrentTime != startTime)
        {
            //Trace.WriteLine("Start Ability Animation.");
            m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.Ability];
            m_ActiveAnimationPlayer.StartClip(m_ClipArr[(int)Vigor.PoseState.Ability]);
            m_IsAnimationLocked = true;
        }

        // Start Orb animation / stuneffect
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Ability] 
        && m_ActiveAnimationPlayer.CurrentTime > m_ClipArr[(int)Vigor.PoseState.Ability].Duration - 
        halfTimeDifference)
        {
            //Trace.WriteLine("Stun Animation Orb Start.");
            // Trigger stuneffect
            m_IsStunEffectActive = true;
            m_Camera.Shake(2.5f, 0.4f);
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorImpact, false);
        }

        // Release lock & stop stuneffect
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Ability] 
        && m_ActiveAnimationPlayer.CurrentTime > m_ClipArr[(int)Vigor.PoseState.Ability].Duration - 
        smallTimeDifference)
        {
            //Trace.WriteLine("Stop Ability Animation.");
            m_IsAnimationLocked = false;

            // Reset Stun effect
            m_IsStunEffectActive = false;
            m_StunEffectCounter = 0;
            m_StunEffectSize = 0.5f;
            m_StunEffectAlpha = 0.5f;
            m_Pose = (int)Vigor.PoseState.Idle;
        }
        break;

    case ((int)Vigor.PoseState.EnergyIdle):
        m_ActiveModel = m_AnimatedModel[(int)Vigor.PoseState.EnergyIdle];
        m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.EnergyIdle];
        break;

    case ((int)Vigor.PoseState.Backwalk):
        TimeSpan leftFootBack = new TimeSpan(0, 0, 0, 0, 200);
        TimeSpan rightFootBack = new TimeSpan(0, 0, 0, 0, 560);
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Backwalk] 
        && (m_ActiveAnimationPlayer.CurrentTime > leftFootBack - smallTimeDifference 
        && m_ActiveAnimationPlayer.CurrentTime < leftFootBack + smallTimeDifference))
        {
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorLeftFoot, false);
        }
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Backwalk] 
        && (m_ActiveAnimationPlayer.CurrentTime > rightFootBack - smallTimeDifference 
        && m_ActiveAnimationPlayer.CurrentTime < rightFootBack + smallTimeDifference))
        {
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorRightFoot, false);
        }
        m_ActiveModel = m_AnimatedModel[(int)Vigor.PoseState.Backwalk];
        m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.Backwalk];
        break;

    case ((int)Vigor.PoseState.StrafingLeft):
        TimeSpan leftFootStrafeLeft = new TimeSpan(0, 0, 0, 0, 240);
        TimeSpan rightFootStrafeLeft = new TimeSpan(0, 0, 0, 0, 480);
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.StrafingLeft] 
        && (m_ActiveAnimationPlayer.CurrentTime > leftFootStrafeLeft - smallTimeDifference 
        && m_ActiveAnimationPlayer.CurrentTime < leftFootStrafeLeft + smallTimeDifference))
        {
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorLeftFoot, false);
        }
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.StrafingLeft] 
        && (m_ActiveAnimationPlayer.CurrentTime > rightFootStrafeLeft - smallTimeDifference 
        && m_ActiveAnimationPlayer.CurrentTime < rightFootStrafeLeft + smallTimeDifference))
        {
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorRightFoot, false);
        }
        m_ActiveModel = m_AnimatedModel[(int)Vigor.PoseState.StrafingLeft];
        m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.StrafingLeft];
        break;

    case ((int)Vigor.PoseState.StrafingRight):
        TimeSpan leftFootStrafeRight = new TimeSpan(0, 0, 0, 0, 240);
        TimeSpan rightFootStrafeRight = new TimeSpan(0, 0, 0, 0, 480);
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.StrafingRight] 
        && (m_ActiveAnimationPlayer.CurrentTime > leftFootStrafeRight - smallTimeDifference 
        && m_ActiveAnimationPlayer.CurrentTime < leftFootStrafeRight + smallTimeDifference))
        {
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorLeftFoot, false);
        }
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.StrafingRight] 
        && (m_ActiveAnimationPlayer.CurrentTime > rightFootStrafeRight - smallTimeDifference 
        && m_ActiveAnimationPlayer.CurrentTime < rightFootStrafeRight + smallTimeDifference))
        {
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorRightFoot, false);
        }
        m_ActiveModel = m_AnimatedModel[(int)Vigor.PoseState.StrafingRight];
        m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.StrafingRight];
        break;

    case ((int)Vigor.PoseState.SprintForwards):
        TimeSpan leftFootSprint = new TimeSpan(0, 0, 0, 0, 0);
        TimeSpan rightFootSprint = new TimeSpan(0, 0, 0, 0, 250);
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.SprintForwards] 
        && (m_ActiveAnimationPlayer.CurrentTime > leftFootSprint - smallTimeDifference 
        && m_ActiveAnimationPlayer.CurrentTime < leftFootSprint + smallTimeDifference))
        {
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorLeftFoot, false);
        }
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.SprintForwards] 
        && (m_ActiveAnimationPlayer.CurrentTime > rightFootSprint - smallTimeDifference 
        && m_ActiveAnimationPlayer.CurrentTime < rightFootSprint + smallTimeDifference))
        {
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorRightFoot, false);
        }
        m_ActiveModel = m_AnimatedModel[(int)Vigor.PoseState.SprintForwards];
        m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.SprintForwards];
        break;

    case ((int)Vigor.PoseState.Dragging):
        m_ActiveModel = m_AnimatedModel[(int)Vigor.PoseState.Dragging];
        m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.Dragging];

        TimeSpan animationPart1 = new TimeSpan(0, 0, 0, 0, 560);
        TimeSpan animationPart2 = new TimeSpan(0, 0, 0, 1);
        TimeSpan animationPart3 = new TimeSpan(0, 0, 0, 1, 400);

        // Between 0 - 560ms Vigor is stuck
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Dragging] 
        && m_ActiveAnimationPlayer.CurrentTime < animationPart1)
        {
            m_IsStuck = true;
        }

        // Between 560ms - 1s Vigor can move
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Dragging] 
        && m_ActiveAnimationPlayer.CurrentTime > animationPart1 
        && m_ActiveAnimationPlayer.CurrentTime < animationPart2)
        {
            m_SoundManager.PlaySound((int)SoundManager.Sounds.VigorRightFoot, false);
            m_IsStuck = false;
        }

        // Between 1s - 1,4s Vigor is stuck again
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Dragging] 
        && m_ActiveAnimationPlayer.CurrentTime > animationPart2 
        && m_ActiveAnimationPlayer.CurrentTime < animationPart3)
        {
            m_IsStuck = true;
        }

        break;

    case ((int)Vigor.PoseState.Deactivating):
        // Start animation & lock state
        if (m_ActiveAnimationPlayer != m_AnimationPlayer[(int)Vigor.PoseState.Deactivating] 
        && m_ActiveAnimationPlayer.CurrentTime != startTime)
        {
            //Trace.WriteLine("Start Ability Animation.");

            m_ActiveAnimationPlayer = m_AnimationPlayer[(int)Vigor.PoseState.Deactivating];
            m_ActiveAnimationPlayer.StartClip(m_ClipArr[(int)Vigor.PoseState.Deactivating]);
            m_ActiveAnimationPlayer.SetCurrentTime(new TimeSpan(0, 0, 0), 0);

            m_IsAnimationLocked = true;
        }

        // Release lock
        if (m_ActiveAnimationPlayer == m_AnimationPlayer[(int)Vigor.PoseState.Deactivating] 
        && m_ActiveAnimationPlayer.CurrentTime > m_ClipArr[(int)Vigor.PoseState.Deactivating].Duration - 
        smallTimeDifference)
        {
            m_IsAnimationLocked = false;
            //m_Pose = (int)Vigor.PoseState.Idle;
        }
        break;
}
// ...

QuantumGame.cs - DrawAnimatedModel

// ...
public void DrawAnimatedModel(Model model, AnimationPlayer animationPlayer, float scale, Matrix projectionMatrix, 
Matrix viewMatrix, float rotation = 0, Vector3 translation = default(Vector3), float alpha=1)
{
    Matrix worldMatrix = Matrix.CreateScale(scale) * Matrix.CreateRotationY(rotation) * 
    Matrix.CreateTranslation(translation);

    Matrix[] bones = animationPlayer.GetSkinTransforms();

    // Render the skinned mesh.
    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (SkinnedEffect skinnedEffect in mesh.Effects)
        {
            skinnedEffect.SetBoneTransforms(bones);

            skinnedEffect.View = viewMatrix;
            skinnedEffect.Projection = projectionMatrix;
            skinnedEffect.World = worldMatrix;

            skinnedEffect.EnableDefaultLighting();

            skinnedEffect.SpecularColor = new Vector3(0.75f);
            skinnedEffect.SpecularPower = 16;

            // Add Fog
            skinnedEffect.FogEnabled = true;
            skinnedEffect.FogColor = new Vector3(0.25f, 0.53f, 0.69f);
            skinnedEffect.FogStart = 150.0f;
            skinnedEffect.FogEnd = 350.0f;

            // Add opacity
            skinnedEffect.Alpha = alpha;
        }

        mesh.Draw();
    }
}
// ...

QuantumGame.cs - Draw

// ...
// Draw Vigor
DrawAnimatedModel(m_Vigor.ActiveModel, m_Vigor.ActiveAnimationPlayer, 0.06f, projectionMatrix, 
viewMatrix, m_Vigor.Rotation, m_Vigor.Position);
// ...