Locomotion

This page is part of the 3D Game Kit sample

The character has several animations that can be blended to allow movement at any speed as well as to turn quickly when needed.

Mecanim

The Mecanim character's LocomotionSM state is a Sub State Machine containing a main Locomotion Blend Tree to allow for movement at any speed, left and right quick turn animations, and the Idle state mentioned on the Idle page:

Animancer

With Animancer, all the state logic is defined in the LocomotionState script which handles everything that applies while the character is moving along the ground except for things that also apply in other states as well (such as the Character.OnAnimatorMove method described on the Movement page). It uses a Mixer State in place of the Blend Tree used by Mecanim:

using Animancer;
using Animancer.Units;
using System;
using UnityEngine;

public class LocomotionState : CharacterState
{
    [SerializeField] private LinearMixerTransition _LocomotionMixer;
    [SerializeField] private ClipTransition _QuickTurnLeft;
    [SerializeField] private ClipTransition _QuickTurnRight;
    [SerializeField, MetersPerSecond] private float _QuickTurnMoveSpeed = 2;
    [SerializeField, Degrees] private float _QuickTurnAngle = 145;

    private AnimatedFloat _FootFall;

    protected virtual void Awake()
    {
        _QuickTurnLeft.Events.OnEnd =
            _QuickTurnRight.Events.OnEnd =
            () => Character.Animancer.Play(_LocomotionMixer);

        _FootFall = new AnimatedFloat(Character.Animancer, "FootFall");
    }

    public override bool CanEnterState => Character.IsGrounded;

    protected virtual void OnEnable()
    {
        Character.Animancer.Play(_LocomotionMixer);
    }

    protected virtual void FixedUpdate()
    {
        if (Character.CheckMotionState())
            return;

        Character.UpdateSpeedControl();
        _LocomotionMixer.State.Parameter = Character.ForwardSpeed;

        UpdateRotation();
        UpdateAudio();
    }

    private void UpdateRotation()
    {
        if (!_LocomotionMixer.State.IsActive)
            return;

        if (!Character.GetTurnAngles(Character.Brain.Movement, out float currentAngle, out float targetAngle))
            return;

        if (Character.ForwardSpeed > _QuickTurnMoveSpeed)
        {
            float deltaAngle = Mathf.DeltaAngle(currentAngle, targetAngle);
            if (Mathf.Abs(deltaAngle) > _QuickTurnAngle)
            {
                ClipTransition turn = deltaAngle < 0 ? _QuickTurnLeft : _QuickTurnRight;

                if (turn.State == null || turn.State.Weight == 0)
                {
                    Character.Animancer.Play(turn);
                    return;
                }
            }
        }

        Character.TurnTowards(currentAngle, targetAngle, Character.CurrentTurnSpeed);
    }

    [SerializeField] private UnityEvent _PlayFootstepAudio;
    private bool _CanPlayAudio;
    private bool _IsPlayingAudio;

    private void UpdateAudio()
    {
        const float Threshold = 0.01f;

        float footFallCurve = _FootFall.Value;
        if (footFallCurve > Threshold && !_IsPlayingAudio && _CanPlayAudio)
        {
            _IsPlayingAudio = true;
            _CanPlayAudio = false;

            _PlayFootstepAudio.Invoke();
        }
        else if (_IsPlayingAudio)
        {
            _IsPlayingAudio = false;
        }
        else if (footFallCurve < Threshold && !_CanPlayAudio)
        {
            _CanPlayAudio = true;
        }
    }
}

Movement Moving the character's position.
Turning Turning the character's rotation.