Apex Path and Mecanim - Article

Checked with version: 4.5

-

Difficulty: Intermediate

This article tutorial corresponds to the video tutorial "Apex Path and Mecanim - Video". A link to this can be found in the Related Tutorials section at the bottom of the page.

Mecanim is Unity’s fantastic module to control and blend animations using a finite state machine. Apex Path lets you utilize the strength of Mecanim to control animations while moving your character. This tutorial shows you how to setup and integrate Apex Path and Mecanim using the Mecanim State Machine. This tutorial assumes you have basic knowledge of Mecanim. For additional information about Unity’s Mecanim system, see the information linked below. This tutorial requires Apex Path to be purchased from the Unity Asset Store. Apex Path can be found here.

Quick Guide

To integrate Mecanim to Apex Path, you need to create a class that implements the IMoveUnits interface. You then set the parameters such as speed and direction that controls the Mecanim state machine from the Move() and the Stop() methods. The following explains in more detail how to set up a scene, where Apex Path and Mecanim are connected to animate a character.

Setting Up the Game World

Create a new project and set up the following Layers: Terrain, Units, Blocks (order is significant) Import your Apex Path package to the project. Then download the Apex Mecanim Tutorial package from the Asset Store and import it. Open the scene “ApexMecanim”. Locate the Robot in the scene and go to the Component menu and click Apex -> QuickStarts -> Navigating Unit with Selection. Set the layer of the robot to “Units”. From the pop-up choose “No, this object only”. Locate the GameWorld object. Locate the Layer Mapping Component and set the Terrain Layer to “Terrain” and the Unit Layer to “Units”.

Create the Animator Controller

Go to the Assets menu in the top bar and click Create - > Animator Controller and name it e.g. “ApexBot2D”. Double-click to open the Animator Controller. Right-click inside the Animator window and select Create State -> From new Blend Tree. Add two float parameters; Speed and Angle. Double-click on the “BlendTree” box. Select the BlendTree box and add four motions: Idle, WalkForward, WalkFwdTurnRight, WalkFwdTurnLeft. The State Machine Tree should look like this:

Alt Apex Path and Mecanim

In the Inspector, rename the BlendTree to “Locomotion”. Set the BlendType to “2D Freeform Cartesian”. Set the Pos X for all but Idle to 3, the Pos Y for WalkFwdTurnRight to 10, and Pos Y for WalkFwdTurnLeft to -10. The Blend Tree should look like this:

Alt Apex Path and Mecanim

If you are in doubt, open the “ApexMecanimComplete” scene to see how the completed tutorial should look. Now the Animator Controller is ready for use.

Using the Animator

We then need to add an Animator to the Robot. Select the Robot in the Hierarchy. Component -> Miscellaneous -> Animator. In the Controller parameter you select the Animator Controller you just made.

Alt Apex Path and Mecanim

We then need to add an Avatar. In this case select “RobotAvatar”. Ensure that “Apply Root Motion” is set to true.

Creating the class that implements the IMoveUnits interface

Before we can use the Animation Controller we just made, we need to set the Speed and Angle Parameters from a script. To do this, create a new C# script that controls the Animator Controller.

Code snippet

namespace Assets.Scripts
{
    using System.Collections;
    using Apex.Steering;
    using UnityEngine;

Make sure the class is using the UnityEngine, System.Collections and Apex.Steering namespaces.

Code snippet

public class MecanimMoverComponent : MonoBehaviour, IMoveUnits

Name the class something logical, and make sure it inherits from Unity’s MonoBehavior and implements the IMoveUnits interface from Apex Path. The IMoveUnits interface ensures that the class overrides the Move() and Stop() methods used by Apex Path to move the unit. Next, we need to declare variables for the class:

Code snippet

public float animatorSpeed = 1f;
private Transform _transform;
private Animator _animator;
private int _speedId;
private int _angleId;

The public animatorSpeed parameter is used to control animation speed. By setting this higher, the unit will move faster. This parameter could also be set via script to control run speeds or connected to the HumanoidSpeed component shipping with Apex Path. But for now it is exposed in the editor so you can experiment with setting different speeds for the animations manually. The private _transform variable is used for caching the transform for performance reasons. The private _animator variable is used for caching the Animator controlling the Animator Controller. The Animator provides the interface to the Speed and Angle parameters that we defined on the Animator Controller. Hence, updating these parameters via the class is basically where we instruct the Mecanim state machine that we defined previously to change states. The private _speedId is used to call the Speed parameter in the state machine. The private _angleId is used to call the Angle parameter in the state machine.

Code snippet

private void Start()
{
_animator = GetComponent<Animator>();
_transform = transform;
_animator.speed = animatorSpeed;
 
_speedId = Animator.StringToHash("Speed");
_angleId = Animator.StringToHash("Angle");
}

In the Start() method we cache the Animator Component from the Robot’s gameObject, we cache the _transform, so we don’t need to call GetComponent every time we need access to the transform, and we set the Animators speed to make sure this is initialized with the value we have set in the Inspector. Furthermore, we cache the reference to the Speed and Angle parameters. Next, we need to implement the Move() and Stop() methods.

Code snippet

public void Move(Vector3 velocity, float deltaTime)
{
float speed = velocity.magnitude;
_animator.SetFloat(_speedId, speed);
 
float angleDirection = TurnDir(_transform.forward, velocity);
float angle = Vector3.Angle(_transform.forward, velocity) * angleDirection;
            
_animator.SetFloat(_angleId, angle);
}

The Move() methods takes the unit’s velocity and the latest deltaTime. The Move() method is called every Update() and thus needs to be fairly high performant to not slow the game if you have many units navigating. The first parameter we calculate and set is the speed. This is calculated as the magnitude (length) of the Unit’s velocity. For this we use the velocity argument and uses Unity’s built-in .magnitude property. Subsequently, we set the Speed parameter that we defined on the Mecanim State Machine using the SetFloat() methods. Next, we need to set the Angle parameter. The Angle parameter determines the difference between the unit’s current heading and the unit’s preferred heading. We use this to simulate centrifugal forces when the unit is turning left or right while walking, and thus control the Walk Forward Right and Walk Forward Left animations. These two animations tilt and rotate the 3d model slightly to the left and right to simulate a natural movement behavior by the unit beyond turning the unit left and right. We calculate the angle between the unit’s current direction (transform.forward) and the unit’s desired velocity using Unity’s built-in Vector3.Angle() method. However, this only gives us a positive float between 0 and 180 corresponding to the degrees between the two vectors. We thus need a way to determine whether the desired velocity is to the left or right of the unit. To determine this we use the utility method TurnDir() to calculate the direction of the angle.

Code snippet

private static float TurnDir(Vector3 p1, Vector3 p2)
{
return Mathf.Sign((p1.z * p2.x) - (p1.x * p2.z));
}

We can then use the angleDirection to make any angle on the left negative by multiple the angle by the angleDirection. Any angle to the left of the unit will be a number between -180 and 0, and any angle to the right will be 0 to 180. Subsequently, we set the Angle parameter on the Mecanim Statement Machine through the SetFloat() method on the Animator. Finally, we need to ensure that the unit stops when it has reached its destination. We do this by setting the Speed parameter to zero when Apex path calls the Stop() method.

Code snippet

public void Stop()
{
_animator.SetFloat(_speedId, 0f);
}

The Complete Script

The complete class is seen here:

Code snippet

namespace Assets.Scripts
{
    using UnityEngine;
    using System.Collections;
    using Apex.Steering;
 
    public class MecanimMoverComponent : MonoBehaviour, IMoveUnits
    {
        public float animatorSpeed = 1f;
        private Transform _transform;
       private Animator _animator;
        private int _speedId;
    private int _angleId;
 
        private void Start()
        {
            _animator = GetComponent<Animator>();
            _transform = transform;
            _animator.speed = animatorSpeed;
 
            _speedId = Animator.StringToHash("Speed");
             _angleId = Animator.StringToHash("Angle");
        }
            
        public void Move(Vector3 velocity, float deltaTime)
        {
            float speed = velocity.magnitude;
            _animator.SetFloat(_speedId, speed);
 
            float angleDirection = TurnDir(_transform.forward, velocity);
            float angle = Vector3.Angle(_transform.forward, velocity) * angleDirection;
            
            _animator.SetFloat(_angleId, angle);
        }
 
        public void Stop()
        {
            _animator.SetFloat(_speedId, 0f);
        }
 
        private static float TurnDir(Vector3 p1, Vector3 p2)
        {
            return Mathf.Sign((p1.z * p2.x) - (p1.x * p2.z));
        }
    }
}

The class is now finished and ready to be added to the Robot. Select the Robot and then choose Components -> Scripts -> Assets.Scripts -> MecanimMoverComponent Start the scene, select the Robot and click somewhere on the grid. The Robot now starts moving.

There is one final task to do however. Right now the unit is actually still turning using the default turning logic of Apex Path. So select the Robot and remove the Turnable Unit Component from it. Now the unit will only turn using the animation, with the limitations that entails (the animation in this example cannot make sharp turns).

Alt Apex Path and Mecanim

Conclusion

In this tutorial, I have shown how the Mecanim state machine, blend trees and animations can be set up to run with Apex Path. I have also shown, how different animations can be controlled using simple inputs such as speed and angle. However, the Mecanim animations, blend trees and state machines can also be used for systems not using Apex Path for inputs on speed and angle. So in short, happy coding and be sure to use the great Mecanim animation system provided by Unity to make those characters come alive.

ApexGameTools

Community author

At ApexGameTools our mission is to make great AI available to everyone. We take AI, pathfinding and steering normally only available to AAA titles, and make it available to indie developers, so we can all enjoy great indie games with great AI. To do this we develop our tools using the highest coding standards and ensure it has great performance and it easy to extend. We ensure it works straight away in Unity projects and integrates with the great Unity technologies, such as Mecanim. We hope you enjoy our software and it helps you make fantastic games.

Related documentation