# 7. Worked Example — Layered Upper-Body Aim + Locomotion Part of [[Animation Advanced]]. Prev: [[6.Animation Events Reliable Patterns]]. Next: [[8.Pitfalls]]. Builds on [[3.Blend Trees]], [[4.Layers Masks Root Motion and Events]], [[6.Worked Example Locomotion Look-At and Hand IK]]. > **NOT compiled in Unity.** ## 7.1 Goal Walk/run with a base-layer Blend Tree while an **Override** upper-body layer aims toward a target. Layer weight fades with aim intent. Optional hand IK from fundamentals part 5/6. ## 7.2 Controller setup 1. **Base Layer:** 2D Blend Tree `Move` (Speed / Angle or MoveX/MoveY) as in [[3.Blend Trees]]. 2. Create Avatar Mask `Mask_UpperBody` — enable Arms, Hands, Head, Upper Chest; **disable** Root, Hips, Legs, Feet. 3. **Layer 1 `Aim`:** Blending Override, Mask = `Mask_UpperBody`, Weight 0, IK Pass on if using `OnAnimatorIK`. 4. Aim layer states: `AimIdle` (empty or subtle breathe) ⇄ `AimRifle` (aim pose clip). Param `Aiming` bool. 5. Parameters: `MoveX`, `MoveY`, `Aiming`, `AimYaw`, `AimPitch` (floats for aim blend tree if you author one). ## 7.3 Scripts ```csharp using UnityEngine; using UnityEngine.InputSystem; /// <summary> /// Drives locomotion floats + aim layer weight. /// NOT compiled in Unity. /// </summary> [RequireComponent(typeof(Animator))] public sealed class AimLocomotionDriver : MonoBehaviour { [SerializeField] Animator animator; [SerializeField] int aimLayer = 1; [SerializeField] float aimWeightDamp = 10f; [SerializeField] InputActionReference move; [SerializeField] InputActionReference aim; // Button static readonly int MoveXHash = Animator.StringToHash("MoveX"); static readonly int MoveYHash = Animator.StringToHash("MoveY"); static readonly int AimingHash = Animator.StringToHash("Aiming"); float _aimW; void OnEnable() { move?.action.Enable(); aim?.action.Enable(); } void OnDisable() { move?.action.Disable(); aim?.action.Disable(); } void Update() { Vector2 m = move != null ? move.action.ReadValue<Vector2>() : Vector2.zero; animator.SetFloat(MoveXHash, m.x); animator.SetFloat(MoveYHash, m.y); bool aiming = aim != null && aim.action.IsPressed(); animator.SetBool(AimingHash, aiming); float target = aiming ? 1f : 0f; _aimW = Mathf.MoveTowards(_aimW, target, aimWeightDamp * Time.deltaTime); animator.SetLayerWeight(aimLayer, _aimW); } } ``` ```csharp using UnityEngine; /// <summary> /// Optional: script IK look/aim while upper-body layer is active. /// Requires Humanoid + IK Pass on aim layer. See Animation part 5. /// NOT compiled in Unity. /// </summary> public sealed class AimIkPass : MonoBehaviour { [SerializeField] Animator animator; [SerializeField] int aimLayer = 1; [SerializeField] Transform aimTarget; [SerializeField] float lookWeight = 0.6f; void OnAnimatorIK(int layerIndex) { if (layerIndex != aimLayer || aimTarget == null) return; float w = animator.GetLayerWeight(aimLayer); animator.SetLookAtPosition(aimTarget.position); animator.SetLookAtWeight(lookWeight * w); } } ``` ## 7.4 Play-mode checklist 1. Move without aiming — full-body locomotion; arms not frozen. 2. Hold aim — upper body eases into aim pose; feet keep Blend Tree motion. 3. Release aim — weight returns to 0; no T-pose (Write Defaults check — part 2). 4. Mask: hips/legs not overridden (watch in Avatar debug / Scene). 5. If using [[Cinemachine 3]] ADS camera, raise ADS cam Priority in sync with `Aiming`. ## 7.5 Extensions - Additive hit layer on chest while aiming (part 1). - `AnimatorOverrideController` for pistol vs rifle aim clips (part 4). - Combat SSM on base for melee that **forces aim weight to 0** on windup. ## Unverified - Exact Humanoid muscle mapping for your character's Upper Chest. - Input System action names in your asset.