If your game character snaps between idle and run with zero transition—or your animation code is a tangled nest of booleans—your Animator Controller is the problem, not the solution. Unity's animation state machine is one of its most powerful systems, but it's also one of the most misunderstood. This guide cuts through the confusion and shows you exactly how to build a clean, scalable animation graph from scratch.
What Is an Animation State Machine?
An Animation State Machine (ASM) is a graph where each node represents an animation clip (a *state*) and each arrow between nodes represents a *transition*—a rule that tells Unity when to switch from one animation to another. Unity's implementation lives in the Animator Controller asset, which you assign to any GameObject with an Animator component.
Every state machine has at least three built-in states:
- Entry: where the graph starts on play
- Exit: terminates a sub-state machine or layer
- Any State: a global source that can transition to any other state
The real power comes from *parameters*—float, int, bool, or trigger values you set from C# scripts that drive transition conditions. Instead of calling `animator.Play("Run")` imperatively, you set `animator.SetFloat("Speed", 5.5f)` and let the graph decide which state to enter.
Setting Up Your Animator Controller
Start with a clean asset. In the Project window, right-click → Create → Animator Controller. Name it after the character, not the action (`PlayerAnimator`, not `RunAnimation`).
Assign it to the character's root GameObject via the Animator component's Controller field. Then open the Animator window (Window → Animation → Animator).
![]()
Create your core parameters first—before adding any states:
```csharp
// Parameters to create in the Animator Controller:
// Speed (Float) — driven by movement input magnitude
// IsGrounded (Bool) — driven by ground check
// Jump (Trigger) — one-shot, consumed on use
// CrouchBlend (Float) — 0 = upright, 1 = full crouch
```
Parameter discipline matters. Floats are best for continuous values (speed, blend weights). Triggers self-reset after one frame—use them for instantaneous actions like jumps or attacks. Booleans are for persistent state (grounded, alive). Avoid int parameters unless you're building a explicit state index system, which is usually a design smell.
Create your base states: Idle, Walk, Run, Jump_Up, Jump_Fall, Land. Set Idle as the default state (right-click → Set as Layer Default State—it turns orange).
Building BlendTrees for Locomotion
Individual states for Idle, Walk, and Run create a problem: you need hard thresholds, which produce visible pops. At Speed = 0.4 you get walk; at 0.41 you get run. BlendTrees solve this by *interpolating* between clips based on a parameter value.
Right-click in the Animator window → Create State → From New BlendTree. Double-click the state to open the BlendTree graph. In the Inspector:
- Set Blend Type to 1D
- Set Parameter to `Speed`
- Click + to add motion fields
- Add: Idle (threshold 0), Walk (threshold 2), Run (threshold 6)
![]()
Enable Automate Thresholds for a first pass, then manually tune for your animations. The thresholds should match the actual speeds those clips were authored for—mismatched thresholds cause foot-sliding.
For strafe movement, switch to 2D Freeform Directional blend type and add a second parameter (`Direction`, -1 to 1). This lets you blend between forward, backward, strafe-left, and strafe-right in a single BlendTree node.
```csharp
// In your PlayerController.cs, drive the BlendTree each frame:
void UpdateAnimator(Vector3 velocity)
{
float speed = new Vector3(velocity.x, 0f, velocity.z).magnitude;
animator.SetFloat("Speed", speed, 0.1f, Time.deltaTime);
float dir = Vector3.Dot(transform.right, velocity.normalized);
animator.SetFloat("Direction", dir, 0.15f, Time.deltaTime);
}
```
The third and fourth arguments to `SetFloat` are the *damp time* and *delta time*—this creates a smooth exponential blend instead of a hard snap. Use damp times between 0.05s and 0.2s for responsive feel.
Transitions and Parameters
Transitions control when and how states change. Select an arrow in the Animator to inspect it. The critical settings:
| Setting | Recommended Value | Why |
|---|---|---|
| Has Exit Time | Off (for input-driven) | Prevents waiting for clip end |
| Exit Time | 0.9–0.95 | For one-shot animations only |
| Transition Duration | 0.1–0.25s | Blend time between clips |
| Interruption Source | Current State | Allows re-triggering same state |
| Can Transition To Self | Off | Prevents restart on same state |
For a Jump trigger, the transition from `Locomotion BlendTree → Jump_Up` should have Has Exit Time: OFF and condition `Jump (trigger)`. The transition from `Jump_Up → Jump_Fall` uses Has Exit Time: ON at exit time 0.9 (let the upward arc finish), plus condition `IsGrounded = false` as a fallback safety check.
Avoid the Any State node for anything other than death or hit reactions. Overusing Any State creates transition spaghetti that's impossible to debug.
Common Mistakes and How to Fix Them
T-pose on play — The default state isn't set, or the Animator Controller isn't assigned. Check the orange state and the Animator component.
Foot sliding in BlendTree — Thresholds don't match clip speeds. Open each clip in the Animation window, check the root motion displacement, and match your threshold values to actual character velocity at each keyframe.
Transition loops — Two states transitioning into each other with no exit condition. Always ensure conditions are mutually exclusive, or add a bool flag to gate re-entry.
Animation stutter at high frame rates — You're calling `SetFloat` every `Update()` but the Animator updates on a fixed culling schedule. Use `animator.updateMode = AnimatorUpdateMode.Normal` and move your parameter-setting code to `Update()`, not `FixedUpdate()`.
Layers not blending correctly — Additive layers need clips authored as additive (relative pose, not absolute). In the Animation Clip import settings, enable Additive Reference Pose and set the reference frame to frame 0 of the base pose.
You can find professionally authored character animation packs—fully rigged with correctly configured root motion—at BitSoul's asset marketplace. Downloading assets with clean rigs and properly configured clips cuts setup time dramatically compared to sourcing from random free sites.
Checklist: State Machine Sanity Before Ship
- [ ] All states reachable from Entry via valid transitions
- [ ] No orphaned states (disconnected nodes)
- [ ] BlendTree thresholds match clip-authored velocities
- [ ] Triggers consumed — no stale trigger values persisting
- [ ] All transitions tested at runtime with Animator debug view open
- [ ] Layer masks assigned (upper body layer masks lower body and vice versa)
- [ ] Avatar Mask created for each additive/override layer
- [ ] `animator.applyRootMotion` toggled deliberately (not left at default)
Building a clean Animator Controller up front saves hours of debugging later. The state machine is your character's behavior contract—treat it like architecture, not an afterthought.
Ready to build on a solid foundation? Browse rigged character assets with animator-ready clip sets at BitSoul's marketplace and skip straight to the fun part.