Summary
The Animator component connects a GameObject to an Animator Controller, which defines animation clips and the transitions between them. Scripts drive the Animator by setting named parameters — booleans, floats, integers, or triggers — that the Animator Controller reads to decide which clip to play and when to transition. The script does not directly choose clips; it sets values and lets the Animator Controller respond.
That division remains the standard production-friendly workflow in current Unity material as well: code exposes state, while the controller and clips own timing, blends, and animation-side transitions. (Unity, The definitive guide to animation in Unity, see source-unity-animation-guide)
Key ideas
Parameter types:
| Method | Parameter type | Behaviour | Use for |
|---|---|---|---|
SetBool(name, value) | Bool | Stays true/false until changed | Sustained states: IsMoving, IsGrounded |
SetFloat(name, value) | Float | Continuous value | Blend tree inputs: Speed |
SetInteger(name, value) | Int | Integer state | Direction facing, weapon index |
SetTrigger(name) | Trigger | Fires once, resets automatically | One-shot events: Hit, Die, Win |
Parameter name constants: Animator parameter names are plain strings. A typo in a string causes silent failure at runtime — the animation simply does not play and no error is logged. Store parameter names as private const string fields so typos become compile errors instead.
GameState enum: Defining a GameState enum (Playing, Won, Lost, Paused) and guarding state-changing methods against invalid transitions prevents duplicate triggers and logical contradictions (e.g. dying while already dead).
Time.timeScale: Setting Time.timeScale = 0f freezes Unity’s time — physics, Update delta, and animations all pause. Setting it back to 1f resumes everything. Always reset timeScale to 1f before loading a new scene, otherwise the new scene starts frozen.
Invoke("MethodName", delay): Schedules a private method to be called after a set number of seconds. Useful for letting a death or win animation play before transitioning to the next scene.
In practice
Caching the Animator:
[SerializeField] private Animator playerAnimator;
private void Start()
{
// Fallback if not assigned in Inspector
if (playerAnimator == null)
playerAnimator = GetComponent<Animator>();
if (playerAnimator == null)
Debug.LogError("[AnimationController] No Animator found!");
}Parameter name constants (prevents silent typo bugs):
private const string PARAM_IS_MOVING = "IsMoving";
private const string PARAM_SPEED = "Speed";
private const string PARAM_HIT = "Hit";
private const string PARAM_DIE = "Die";
private const string PARAM_WIN = "Win";Driving movement animation each frame:
private void HandleMovementAnimation()
{
if (playerAnimator == null) return;
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
bool isMoving = (h != 0f || v != 0f);
playerAnimator.SetBool(PARAM_IS_MOVING, isMoving);
playerAnimator.SetFloat(PARAM_SPEED, new Vector2(h, v).magnitude);
}One-shot trigger (hit reaction):
public void PlayHitAnimation()
{
if (playerAnimator == null) return;
playerAnimator.SetTrigger(PARAM_HIT); // fires once, resets automatically
}Game state enum with state-guarded transitions:
public enum GameState { Playing, Won, Lost, Paused }
private GameState currentState = GameState.Playing;
public void TriggerGameOver()
{
if (currentState != GameState.Playing) return; // guard
currentState = GameState.Lost;
playerAnimator.SetTrigger(PARAM_DIE);
Invoke("LoadGameOverScene", transitionDelay); // delay for animation
}
private void LoadGameOverScene()
{
SceneManager.LoadScene(gameOverSceneName);
}Pause / unpause:
public void TogglePause()
{
if (currentState == GameState.Playing)
{
currentState = GameState.Paused;
Time.timeScale = 0f; // freeze everything
}
else if (currentState == GameState.Paused)
{
currentState = GameState.Playing;
Time.timeScale = 1f; // resume
}
}
public void RestartScene()
{
Time.timeScale = 1f; // always restore before loading
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}Gotchas
SetTriggerfires once and resets. If the Animator Controller has no transition that consumes it, the trigger accumulates and fires multiple times unexpectedly. Ensure every trigger has at least one outgoing transition.Invokematches the method name by string. A renamed method will not cause a compile error — the invocation will silently do nothing at runtime. Test after any rename.Time.timeScale = 0fstopsUpdatedelta time but not all code. Coroutines usingWaitForSecondsRealtimeare unaffected; those usingWaitForSecondsare paused.- Never call
SetTriggerevery frame for a sustained state — useSetBoolinstead. A trigger called every frame will queue indefinitely in the Animator.
Related
- unity-animator-controller — the editor-side animation state machine this scripting layer drives
- monobehaviour-lifecycle —
Updatedrives per-frame animation updates;Startcaches the Animator - unity-getcomponent — fallback
GetComponent<Animator>()pattern - unity-gamemanager-pattern —
SceneManager.LoadSceneand game state management - csharp-control-flow —
enumand state guardifchecks