Mecanim: One Blend Tree To Rule Them All!

I just stumbled upon a blog post of scott petrovic where he pointed out that one can use one blend tree in unity to blend seamlessly between idle, strafe and forward / backward movement (http://www.scottpetrovic.com/blog/2013/03/mecanim-directional-blend-trees-concept-art-for-my-game/comment-page-1/).

So I rebuilt my character controller script from scratch to fit the new blend tree (and also, to use unity 5 animation behaviours):

Idle and movement blend tree
Idle and movement blend tree which blends between idle, left/right strafe and forward/backward movement

The script just computes the parameters VerticalMotion and HorizontalMotion which is then used by the blend tree to blend between the five different animations.

Since my character is placed in a 3d top-down dungeon hack and slay game, I had to compute those values upon two inputs: Movement direction (WASD / Left Stick) and LookAt direction (Arrow Keys / Right Stick).

My character can normally walk forward by pressing the movement controls or walk in the desired direction but looking at a specific target – for example, if the player wants to look at a large group of mobs but wants to walk away from them, he can keep shooting fireballs at them while moving backwards.

To use the root motion I attached a custom MovementBehaviour (derived from StateMachineBehaviour ) script to the blend tree.

/// <summary>
/// This script must be attached to any movement animation to enable custom root motion processing
/// </summary>
public class MovementBehaviour : StateMachineBehaviour {

    /// <summary>
    /// Gets or sets a motion multiplier used to scale the motion reported to the character controller script
    /// </summary>
    public float MotionMultiplier = 1.0f;

    /// <summary>
    /// OnStateMove is called right after Animator.OnAnimatorMove(). 
    /// Code that processes and affects root motion should be implemented here
    /// </summary>
    /// <param name="animator">The attached animator</param>
    /// <param name="stateInfo">The animators state info</param>
    /// <param name="layerIndex">The layer</param>
    public override void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
         base.OnStateMove(animator, stateInfo, layerIndex);
 
         // Hint: Search component is an extension method searching for a component up and down the child/parent tree
         var characterController = animator.gameObject.SearchComponent<CharacterBaseController>();
         if (characterController != null) {
             // This gets the root motion curve speed of the current animation
             var motionSpeed = animator.GetFloat("RunSpeed") * MotionMultiplier;
 
             // Here we call our character controller which then moves the rigid body around
             characterController.onAnimatorMove(animator, stateInfo, layerIndex, motionSpeed);
         } else {
             DebugHelper.LogFormat("Animator {0}: Missing component CharacterBaseController.", animator);
         }
    }
}

Inside my CharacterBaseController class I then use the provided information to apply the motion to my rigid body:

/// <summary>
/// onAnimatorMove is called from MovementBehaviour in OnStateMove. 
/// </summary>
/// <param name="animator">The attached animator</param>
/// <param name="stateInfo">The animators state info</param>
/// <param name="layerIndex">The layer</param>
public void onAnimatorMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, float motionSpeed) {
    // This moves the rigid body in the requested movement direction (movementVelocity) computed in FixedUpdate.
    if (RigidBody != null) {
        var velocity = movementVelocity * Math.Abs(motionSpeed) * MovementSpeed * Time.deltaTime;
        RigidBody.AddForce(velocity, ForceMode.VelocityChange);
    } 
}

To use root motion, one need to define animation curves for all animations which should move the character:

root_motion

Instead of scaling the speed in my script for slower backward / strafe movement I simply scaled down their curves, so the velocity gets automatically scaled down when the character moves backwards or sideways.

 

 

Visit Us On FacebookVisit Us On Google PlusVisit Us On YoutubeVisit Us On Twitter

Leave a Reply