# 3. Root Motion Advanced Part of [[Animation Advanced]]. Prev: [[2.Sync Layers Write Defaults and Interruption]]. Next: [[4.MatchTarget CrossFade Rebind OverrideController]]. Basics: [[4.Layers Masks Root Motion and Events]]. Conflicts: [[Surface-Walking with an Omnidirectional Player Movement]], [[Car Controller]]. **Docs:** [Scripting Root Motion](https://docs.unity3d.com/6000.5/Documentation/Manual/ScriptingRootMotion.html) · [OnAnimatorMove](https://docs.unity3d.com/6000.5/Documentation/ScriptReference/MonoBehaviour.OnAnimatorMove.html) ## 3.1 Authority models | Mode | Who moves the character | |---|---| | Apply Root Motion **On** | Animator applies `deltaPosition` / `deltaRotation` | | **Handled by Script** | You implement `OnAnimatorMove` (Animator detects the method) | | Off + in-place clips | Your CharacterController / Rigidbody / custom mover only | Pick one authority. Mixing CharacterController `.Move` *and* unhandled root motion causes slides and double motion — a common conflict with [[Surface-Walking with an Omnidirectional Player Movement]] and vehicle seats ([[Car Controller]]). ## 3.2 OnAnimatorMove — slopes and control ```csharp using UnityEngine; /// <summary> /// Apply root motion with ground projection and optional external motion. /// NOT compiled in Unity. /// </summary> [RequireComponent(typeof(Animator))] public sealed class RootMotionApplicator : MonoBehaviour { [SerializeField] Animator animator; [SerializeField] CharacterController controller; [SerializeField] LayerMask groundMask = ~0; [SerializeField] float slopeStickForce = 5f; [SerializeField] bool projectOnGround = true; Vector3 _external; // moving platform / knockback for this frame void Awake() { if (animator == null) animator = GetComponent<Animator>(); // Presence of OnAnimatorMove => Apply Root Motion shows Handled by Script } public void AddExternalMotion(Vector3 worldDelta) => _external += worldDelta; void OnAnimatorMove() { Vector3 delta = animator.deltaPosition + _external; _external = Vector3.zero; if (projectOnGround && controller != null) { // Simple stick-to-ground: push down a bit when grounded if (controller.isGrounded) delta += Vector3.down * slopeStickForce * Time.deltaTime; } if (controller != null) controller.Move(delta); else transform.position += delta; transform.rotation *= animator.deltaRotation; } } ``` ## 3.3 Moving platforms Root motion is in **world/actor space as evaluated** — it does not automatically inherit a platform's motion. Patterns: 1. **Parent** the character to the platform (simple; fights some Controllers). 2. **Add platform delta** each FixedUpdate/Update into `_external` (above). 3. Disable root motion on platforms and drive fully from physics (vehicles — [[Car Controller]]). ```csharp using UnityEngine; public sealed class PlatformRider : MonoBehaviour { [SerializeField] RootMotionApplicator root; Transform _platform; Vector3 _lastPlatformPos; void OnControllerColliderHit(ControllerColliderHit hit) { if (!hit.collider.CompareTag("MovingPlatform")) return; if (_platform != hit.transform) { _platform = hit.transform; _lastPlatformPos = _platform.position; } } void LateUpdate() { if (_platform == null || root == null) return; Vector3 delta = _platform.position - _lastPlatformPos; _lastPlatformPos = _platform.position; root.AddExternalMotion(delta); } public void ClearPlatform() => _platform = null; } ``` *(Tag/`OnControllerColliderHit` approach is illustrative — verify against your mover.)* ## 3.4 Slopes and foot slide - Author clips with appropriate Root Transform rotation / bake options (fundamentals part 4). - On steep slopes, pure horizontal root deltas climb poorly — project onto ground plane or blend to in-place + mover. - `animator.gravityWeight` interacts with grounded logic; do not invent values — read current API when tuning. ## 3.5 When to turn root motion off - Precision platforming with custom acceleration curves ([[Surface-Walking with an Omnidirectional Player Movement]]). - Networked movement where server owns transform ([[Multiplayer with Netcode for GameObjects]] — separate topic). - Seated / driving states ([[Car Controller]]). Use Animator bool `UseRootMotion` driven by the brain, toggled in `OnAnimatorMove` or by setting `animator.applyRootMotion`. ```csharp public void SetRootMotion(bool on) => animator.applyRootMotion = on; ``` Note: with `OnAnimatorMove` present, Inspector shows Handled by Script — you still decide whether to consume `deltaPosition`. ## Unverified - Exact interaction of `gravityWeight` with your CharacterController gravity solution. - Physics-based Rigidbody root motion (use `Rigidbody.MovePosition` carefully; not expanded here).