Unity6 Game Development · Lesson 29

Animator parameters are not transition graphs

Drive a declared Boolean while keeping controller and clip assumptions explicit.

Unity 6 Editor walkthrough

The Unity script needs the Editor and the setup listed below. The separate C# companion practice runs in SharpRune without UnityEngine.

Treat an animation controller as authored data

A gameplay intention such as moving is not the same thing as an animation state. The controller decides which states exist, which clips they play, which parameters their transitions inspect and when those transitions are permitted. This lesson sends one Boolean to a deliberately prepared controller so each boundary remains visible. A log saying Moving is true is evidence about the parameter write, while the Animator window and Game view provide separate evidence about state evaluation and visual output. That separation makes a missing transition diagnosable without turning every problem into a guessed C# timing error or adding unrelated input code.

Validate the declared parameter before writing it

Awake resolves the Animator required on the same object and rejects a missing runtime controller. It walks the controller’s parameters to find the cached Moving hash with Boolean type; a float of the same name is not accepted. ToggleMoving then checks Play mode, activation and that validated configuration before changing the local intention and calling SetBool. StringToHash provides an API identifier, not an independently authored portable hashing contract. The local moving field persists across this component’s disable and enable cycle; if another system writes the same parameter, a production adapter would need a single ownership policy rather than two contradictory local sources of truth.

    private void Awake()
    {
        animator = GetComponent<Animator>();
        if (animator.runtimeAnimatorController == null)
        { Debug.LogError("Assign the prepared Animator Controller.", this); enabled = false; return; }
        foreach (AnimatorControllerParameter parameter in animator.parameters)
            if (parameter.nameHash == MovingId && parameter.type == AnimatorControllerParameterType.Bool) configured = true;
        if (!configured) { Debug.LogError("Controller needs the Moving Boolean.", this); enabled = false; }
    }

Keep a gameplay predicate smaller than the animation system

The companion’s input facts are speed and grounded. It returns Move only when the actor is grounded and the supplied speed is greater than one tenth. A zero-speed grounded actor is Idle, a speed of two with grounded true is Move, and the same speed with grounded false returns Idle under this small policy. That final result is not a claim that every game should idle in mid-air; a real design might add an airborne intention. The lab does not evaluate transition duration, blend trees, layers or clip events. Its purpose is to make one intention predicate independently testable before that result is adapted into controller parameters.

Exit times, transition conditions and root motion can all matter

For the immediate baseline, turn Has Exit Time off on both transitions and use visible looping clips. Otherwise a correct parameter update can wait for an authored exit condition or take time to blend, which is not the same as ignoring the parameter. Verify that the Animator uses the intended controller and that the default state is Idle. Keep Apply Root Motion off while another system owns the object’s pose. A clip can also animate a property unexpectedly and compete with a movement script. The debugging question is which system owns each visible effect, not whether another SetBool call every frame would somehow force the desired image.

Practice: change one controller condition at a time

Run the prepared two-state controller and observe the Moving value, highlighted state and clip effect together. Then stop and enable Has Exit Time on one transition only. Predict why the next toggle can be reflected in the parameter before its visual transition occurs. Restore the baseline and deliberately rename the parameter or change its type in a copied controller to inspect the script’s configuration error. Keep each experiment isolated: changing the clip, parameter, transition and input source together destroys the evidence needed to explain a difference. Record the controller asset used, because two components with similar names can still reference different authored graphs.

  • Hint: the Animator window in Play mode can distinguish a parameter write from an active-state change.
  • Hint: restore the exact Boolean Moving contract before exploring timing and layers.

Practice: expand the intention model with an explicit third state

Test the threshold exactly at one tenth and slightly above it, then test a negative speed if the domain promises a magnitude rather than signed velocity. Decide whether invalid speed should be rejected or normalized. Add an Airborne intention for grounded false without pretending that a corresponding Unity state already exists. Write expected outputs for that new pure decision, then list the controller parameters and transitions an engine adapter would need to represent it. This exercise is independent of modifying Has Exit Time: one changes the gameplay decision vocabulary, while the other changes how a real controller consumes a parameter over its own evaluation schedule.

  • Hint: a strict greater-than threshold excludes the exact boundary.
  • Hint: adding a string result in C# does not create an Animator state or clip.

Review parameter identity, state data and visible ownership

A useful handoff names the Moving Boolean, the assigned controller, default state, both transition conditions, exit-time settings and root-motion policy. The component can validate some of those assumptions, while the scene and controller assets hold the rest. The supplied source is a real Unity6000.0 MonoBehaviour in C#9 syntax; no fake Animator was compiled to claim semantic integration. The portable predicate was executed on desktop .NET, and no local Unity Editor or Player run is claimed. Preserve those distinct receipts when converting the simple toggle into a production locomotion adapter driven by a tested gameplay model.

Runs in Unity 6 Editor

Unity Editor example

Set up in Unity 6

  1. Create a Cube with an Animator and attach AnimatorBoolProbe.cs. Use a clean object whose animation will not fight another Transform writer.
  2. Create an Animator Controller with exact Boolean parameter Moving. Create looping Idle and Move clips that visibly differ, for example two local scale values, and assign the clips to two states.
  3. Make Idle the default state. Create Idle -> Move with Moving true and Move -> Idle with Moving false; turn off Has Exit Time on both for this baseline and choose a short transition duration.
  4. Assign the Controller to the Animator, turn Apply Root Motion off, enter Play mode, and invoke Toggle Moving from the script context menu. Observe both the parameter and highlighted controller state.
  5. Test a misspelled parameter or wrong parameter type in a copy to see the explicit configuration error. Restore the known controller before exploring exit time, blend duration or animation layers.

AnimatorBoolProbe.cs

Download Unity script ↗
using UnityEngine;
[RequireComponent(typeof(Animator))]
public sealed class AnimatorBoolProbe : MonoBehaviour
{
    private static readonly int MovingId = Animator.StringToHash("Moving");
    private Animator animator;
    private bool configured;
    private bool moving;
    private void Awake()
    {
        animator = GetComponent<Animator>();
        if (animator.runtimeAnimatorController == null)
        { Debug.LogError("Assign the prepared Animator Controller.", this); enabled = false; return; }
        foreach (AnimatorControllerParameter parameter in animator.parameters)
            if (parameter.nameHash == MovingId && parameter.type == AnimatorControllerParameterType.Bool) configured = true;
        if (!configured) { Debug.LogError("Controller needs the Moving Boolean.", this); enabled = false; }
    }
    [ContextMenu("Toggle Moving (Play Mode)")]
    public void ToggleMoving()
    {
        if (!Application.isPlaying || !isActiveAndEnabled || !configured) return;
        moving = !moving;
        animator.SetBool(MovingId, moving);
        Debug.Log("Moving parameter: " + moving, this);
    }
}

What to observe in Unity

A correctly configured controller reacts to the Moving Boolean through its authored transitions. The script rejects a missing controller or wrong parameter; it does not create clips or guarantee immediate transitions in arbitrary controllers.

The console practice below isolates the C# logic. It does not run a Unity scene or emulate Unity engine callbacks.

C# companion practice

using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;

string Intent(decimal speed, bool grounded) => grounded && speed > 0.1m ? "Move" : "Idle";
Console.WriteLine(Intent(0m, true));
Console.WriteLine(Intent(2m, true));
Console.WriteLine(Intent(2m, false));
Open C# practice

C# practice output

Idle
Move
Idle
Try this
A matching parameter and a transition that consumes it are separate requirements.

Further reading