Unity Workflow LibraryUWLAbout

Topic 9 · Animation

Pitfalls: animation & rigs

One driver per Animator, culled poses, masks.

Generalized pitfalls from shipped and active projects. Record your own project's traps the same way: Keeping a project pitfall file. All entries LOCKED evidence. Status: locked (empirical, seen in shipped/active projects).

  • ONE-LOCOMOTION-DRIVER — two scripts writing the same Animator parameters stack silently. Why: no ownership of the parameter set. Check: one component owns each Animator parameter; grep for every SetFloat/SetBool on it. Status: locked (empirical, seen in shipped/active projects).
  • REGENERATE-CONTROLLERS-IN-PLACE — rebuilding an AnimatorController with delete+create gives it a new GUID and breaks every prefab reference. Why: references are by GUID. Check: generators clear and refill the existing asset; a test asserts the GUID is unchanged. Status: locked (empirical, seen in shipped/active projects).
  • CULLED-ANIMATORS-DO-NOT-POSE — with CullUpdateTransforms an off-screen Animator writes no bones, so headless/off-camera checks read rest pose. Why: the correct runtime culling mode skips transform writes when not visible. Check: verify animation with the subject in view, or temporarily set AlwaysAnimate in the test only. Status: locked (empirical, seen in shipped/active projects).
  • VERIFY-MOTION-NOT-PARAMETERS — "parameters are live" is not "the character animates". Why: the check read inputs, not the pose. Check: assert bone transforms change across frames (or a still from a running clip). See also FEATURE-DUE-DILIGENCE. Status: locked (empirical, seen in shipped/active projects).
  • DISCARDED-ROOT-MOTION-SWAYS — discarding root motion with the default humanoid root reference leaves a rigid stride-frequency counter-sway. Why: the clip's root motion was baked into the body instead of the root. Check: set the clip's root transform bake options (rotation/Y/XZ "Bake Into Pose" with the right basis) when root motion is discarded. Status: locked (empirical, seen in shipped/active projects).
  • ANIM-SPEED-FROM-OBSERVED-VELOCITY — on networked clients, drive locomotion speed from the observed transform velocity, not a replicated command. Why: the replicated intent and the interpolated position disagree in time. Check: client animation speed = smoothed delta position of the drawn transform. Status: locked (empirical, seen in shipped/active projects).
  • REPLICATE-CAUSES-NOT-EFFECTS — replicate gameplay signals (state flags, target), never look direction, layer weights, clip times or bones. Why: effects are deterministic from causes. Check: ghost/NetworkVariable fields on animated units are only gameplay signals. Status: locked (empirical, seen in shipped/active projects).
  • ONE-SHOTS-ARE-EDGE-TRIGGERS — fire attack/one-shot triggers on the rising edge of a replicated flag, priming the baseline on first sight. Why: level-triggered firing and stale triggers. Check: holds use bools, one-shots use triggers set on edge only; first observation primes without firing. Status: locked (empirical, seen in shipped/active projects).
  • HUMANOID-MASKS-ON-OPTIMIZED-SKELETONS-ARE-EMPTY — on optimized (flattened) skeletons, humanoid body-part masks bake to zero bones. Why: only transform-based avatar masks survive optimization. Check: use transform avatar masks with optimized skeletons. Status: locked (empirical, seen in shipped/active projects).
  • NO-REBIND-ON-MODULAR-RIGS — Animator.Rebind on a modular optimized rig snaps each attached SkinnedMeshRenderer to its raw import bind. Why: rebind re-resolved bindings against the import pose. Check: never call Rebind on modular characters; rebuild the character instead. Status: locked (empirical, seen in shipped/active projects).
  • THROTTLE-SKINNED-SPAWNS — instantiating many skinned characters in one frame can stall or crash the GPU driver. Why: skin/skeleton init cost landed in one frame. Check: cap new skinned instances per frame (single digits) and queue the rest. Status: locked (empirical, seen in shipped/active projects).
  • PREVIEW-GRAPH-MUST-STAY-ALIVE — destroying a PlayableGraph/AnimationMode preview snaps a humanoid back to rest pose. Why: the pose exists only while the graph samples. Check: keep the graph alive through the capture; re-sample after domain reload. Status: locked (empirical, seen in shipped/active projects).
  • IMPORT-SCALE-LEAKS-INTO-THE-AVATAR — an avatar created while the model had a wrong import scale keeps that scale's units. Why: CreateFromThisModel captured positions at the old scale. Check: fix scale first, then (re)create the avatar; compare a known bone length. Status: locked (empirical, seen in shipped/active projects).
  • SKIN-HAT-GEOMETRY-TO-THE-HEAD — nearest-bone auto-skinning assigns wide head attachments (caps, brims, horns) to the shoulders. Why: nearest-segment binding picks clavicle/upper-arm for verts at shoulder width. Check: weight extra-cranial geometry 100% to the head; normalizing total height must exclude it. Status: locked (empirical, seen in shipped/active projects).