Animator state machine
Assets/
Scenes/
Arena.unity
Scripts/
Animations/
you already havethis lesson adds
The Knight now moves with PlayerMovement, but it slides around frozen in a T-pose. This lesson gives it legs.
Your code never plays a clip directly. It sets parameters; the Animator decides which clip that means and blends between them.
Watch the character on the left act out whichever state is lit on the right — and the three parameters at the bottom are the only things your code ever touches.
Set up the controller
- 1
Project: , name it AC_Knight.
- 2
Select the Knight, add an Animator component, drag AC_Knight into Controller and the model's Avatar into Avatar.
- 3
Open the window and drag your Idle clip in — it becomes the orange default state.
- 4
In the Parameters tab add: Speed (Float), IsGrounded (Bool), Attack (Trigger).
A blend tree beats separate walk/run states
- 1
Right-click in the Animator → Create State → From New Blend Tree. Name it Locomotion and make it the default.
- 2
Double-click it. Set the Blend parameter to Speed, then add three motions: Idle at 0, Walk at 2, Run at 6.
- 3
Now one float smoothly crossfades idle → walk → run. No transition arrows needed for locomotion at all.
Driving it from code
using UnityEngine;
[RequireComponent(typeof(Animator))]
public class PlayerAnimator : MonoBehaviour
{
private Animator anim;
private CharacterController cc;
// hashes are faster than strings
private static readonly int SpeedHash = Animator.StringToHash("Speed");
private static readonly int GroundedHash = Animator.StringToHash("IsGrounded");
private static readonly int AttackHash = Animator.StringToHash("Attack");
void Awake()
{
anim = GetComponent<Animator>();
cc = GetComponent<CharacterController>();
}
void Update()
{
// horizontal speed only — vertical fall must not look like running
Vector3 flat = new Vector3(cc.velocity.x, 0f, cc.velocity.z);
anim.SetFloat(SpeedHash, flat.magnitude, 0.1f, Time.deltaTime); // 0.1f = damping
anim.SetBool(GroundedHash, cc.isGrounded);
if (Input.GetMouseButtonDown(0))
anim.SetTrigger(AttackHash);
}
}The third and fourth arguments of SetFloat are damp time and delta time: the value eases instead of jumping, so the blend never pops.
A Trigger is a bool that resets itself after being consumed — exactly right for one-shot actions like Attack.
StringToHash once in a static field: string lookups every frame are wasted work.
Transition settings that fix 90% of jank
Uncheck Has Exit Time on any transition that must react to the player (attack, jump). Leave it on for animations that must finish.
Transition Duration 0.1–0.2 s feels responsive. Above 0.3 s the character feels drunk.
Interruption Source → Current State lets a new attack cut into the previous one instead of queueing.
Animation Events — hit exactly on the swing
Damage should not apply when you press the button; it should apply when the sword is actually out. Animation Events call a method at a chosen frame of the clip.
// on the same GameObject as the Animator
public void OnAttackHit() // pick this name in the clip's Events row
{
Debug.Log("hit frame");
// damage the enemies in range
}