# 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.