Every animation clip an artist makes is frozen the moment it is exported: a walk cycle always plants its feet the same way, a hit reaction always flinches by the same amount, an idle pose always breathes at the same rate. That is fine until the game asks a question the clip was never built to answer — exactly where is the ground under this foot right now, exactly where is the player standing so the boss can look at them, what does this character's body do the instant a rocket sends it flying backward. This chapter is about generating motion with code and physics instead of (or on top of) a pre-made clip, so a character can react correctly to a game world an animator could never fully predict in advance.
You already know skeletal animation and clip playback from 9.1, blending and state machines from 9.2, IK and root motion from 9.3, and how Timeline sequences scripted moments from 9.4. This chapter adds a different kind of motion source: instead of sampling a clip, you compute a rotation, a position, or a whole physics simulation, fresh, every single frame. It leans directly on interpolation and easing from chapter 2.4, and the spring math and numerical integration from chapter 2.5 — if either of those feels rusty, this is a good moment to skim them again, because several sections below reuse that math almost line for line.
Say a boss character should turn its head to track the player. An animator could author a handful of "look left", "look right", "look up" clips and blend between them (chapter 9.2's territory), but that only ever approximates a few fixed directions — the player can stand literally anywhere, and the head needs to point exactly at them, continuously, every frame. Authoring infinite clips is not an option. Computing a rotation from the target's live position, every frame, is.
using UnityEngine;
public class LookAtNaive : MonoBehaviour
{
public Transform head; // the bone to rotate (e.g. the head, or the topmost spine bone)
public Transform target; // what to look at, e.g. the player
public float turnSpeed = 200f; // degrees per second the head is allowed to turn
[Range(0f, 1f)] public float weight = 1f; // how much of the look-at to apply on top of the animation
void LateUpdate()
{
Vector3 toTarget = target.position - head.position;
Quaternion desiredRotation = Quaternion.LookRotation(toTarget, Vector3.up);
Quaternion animatedRotation = head.rotation; // whatever the Animator already set this frame
Quaternion turnedRotation = Quaternion.RotateTowards(animatedRotation, desiredRotation, turnSpeed * Time.deltaTime);
head.rotation = Quaternion.Slerp(animatedRotation, turnedRotation, weight);
}
}
This lives in LateUpdate() for exactly the reason the camera chapter gave: Unity evaluates the Animator and writes this frame's animated pose in between Update() and LateUpdate(), so by the time this code runs, head.rotation already holds the fully animated rotation for the frame. Reading it here, instead of in Update(), guarantees this script adjusts the final pose rather than a stale one from last frame.
Quaternion.LookRotation(toTarget, Vector3.up) builds a rotation that faces along toTarget, using world up as the reference for "up" (explained fully back in the rotations chapter, 2.3). Quaternion.RotateTowards turns the head at a constant angular speed, clamped to turnSpeed * Time.deltaTime degrees this frame — this is the fix from chapter 2.4's smoothing pitfall, deliberately used here instead of a raw Slerp(a, b, speed * Time.deltaTime), because RotateTowards can never overshoot the target no matter how large Time.deltaTime gets on a slow frame. The final Slerp(animatedRotation, turnedRotation, weight) is a different job entirely: it is not smoothing over time, it is blending by a fixed amount, so a designer can fade the whole look-at in and out (weight 0 = pure animation, weight 1 = full look-at) without touching the turn speed at all.
There is no console output here — this script drives a Transform continuously. In plain words, here is what happens over a few frames if the target suddenly appears 90 degrees to the character's right while the head is facing forward: frame 1 the head turns turnSpeed * Time.deltaTime degrees toward it, frame 2 it turns the same amount again, and so on, until the remaining angle is smaller than one frame's step, at which point it lands exactly on the target and stops.
LookAtNaive exactly as written above. It works perfectly as long as the target stays roughly in front of the character. The moment the target walks behind the character, LookRotation happily computes a rotation that spins the head a full 150-180 degrees to face it — which, on a human-shaped rig, means the head rotating clean through the shoulders. Nothing in this script stops that. The next section fixes it.Real necks do not rotate freely — they have a limited range of motion around whatever direction the spine is already facing. The fix is a cone limit: pick a "rest direction" (usually the spine's forward), measure the angle between that direction and the target, and if the angle is bigger than some maximum, aim at the nearest point on the edge of the cone instead of at the real target.
using UnityEngine;
public class LookAtClamped : MonoBehaviour
{
public Transform head;
public Transform spine; // defines the "forward" the cone is centered on
public Transform target;
public float turnSpeed = 200f;
public float maxConeAngle = 60f; // degrees the head may turn away from spine.forward
[Range(0f, 1f)] public float weight = 1f;
void LateUpdate()
{
Vector3 desiredDir = (target.position - head.position).normalized;
Vector3 restDir = spine.forward;
float angle = Vector3.Angle(restDir, desiredDir);
Vector3 clampedDir = angle > maxConeAngle
? Vector3.RotateTowards(restDir, desiredDir, maxConeAngle * Mathf.Deg2Rad, 0f)
: desiredDir;
Quaternion desiredRotation = Quaternion.LookRotation(clampedDir, Vector3.up);
Quaternion animatedRotation = head.rotation;
Quaternion turnedRotation = Quaternion.RotateTowards(animatedRotation, desiredRotation, turnSpeed * Time.deltaTime);
head.rotation = Quaternion.Slerp(animatedRotation, turnedRotation, weight);
}
}
Vector3.Angle(a, b) returns the unsigned angle between two directions, from 0 to 180 degrees. When that angle exceeds maxConeAngle, Vector3.RotateTowards(restDir, desiredDir, maxConeAngle * Mathf.Deg2Rad, 0f) rotates restDir toward desiredDir by exactly maxConeAngle (note: this overload takes radians, hence the Deg2Rad conversion) and a magnitude change of 0 (both vectors are already unit length — we only want a new direction, not a new length). The result sits precisely on the cone's edge, on the side closest to the real target. Everything after that line is identical to section 1.
Here is a worked trace of target B from the diagram — 70 degrees off spine forward, cone limit 45 degrees, so the clamp is definitely active:
// target is 70 degrees off spine.forward, cone limit maxConeAngle = 45
// clampedAngle = min(70, 45) = 45 -- the clamp kicks in, head aims at the cone edge instead
// turnSpeed = 200 deg/s, dt = 0.1 (a bigger dt than a real frame, just to keep this table short)
//
// frame currentAngle (degrees from spine.forward)
// 0 0.00 -- this frame's animated pose has the head facing forward
// 1 20.00 -- moved the max allowed step: 200 * 0.1 = 20 degrees
// 2 40.00 -- moved another 20 degrees
// 3 45.00 -- only 5 degrees of room left before the clamp, so it stops there
// 4 45.00 -- held at the cone edge; it only moves again if the target does
weight toward 0 as the target angle approaches or passes the cone limit, instead of holding the head pinned at the edge forever — a character that stares fixedly at your shoulder blade for ten seconds because you walked slightly behind it looks broken, not attentive. A simple version: weight *= 1f - Mathf.InverseLerp(maxConeAngle * 0.5f, maxConeAngle, angle), reusing InverseLerp straight out of chapter 2.4.Vector3.up into Quaternion.LookRotation (a target directly overhead or directly underfoot). The forward and up vectors becoming parallel is a degenerate case with no well-defined rotation, and Unity will log a warning and hand back something you probably did not want. Guard it: if toTarget is nearly parallel to your chosen up vector, either skip the look-at for that frame or swap in a different up reference.Hair, ponytails, capes, coat tails, jewelry, ears, tails — characters, and especially anime-style characters, are covered in things that should not move perfectly rigidly with the body. When the head snaps around fast, hair should lag behind for a moment and swing past its resting position before settling. That lag-and-overshoot is called secondary motion — the "follow-through" and "overlapping action" traditional animators have hand-drawn for a century, generated automatically here instead. A bone that does this to itself is usually called a spring bone or dynamic bone (the exact name varies by engine and by which package popularized it, but the idea is the same everywhere).
Every spring bone tracks a rest position — where the bone would sit if it followed its parent perfectly rigidly, recomputed fresh from the parent's current transform every frame. The bone's actual position is pulled toward that rest position by a spring, and resisted by a damper. This is precisely the spring from chapter 2.5's Hooke's-law example, with one new ingredient.
Compare this to chapter 2.5's spring: there, acceleration was just a = -k/m * x, and the frictionless spring oscillated forever because nothing removed energy. Add a damping term -c * v (a force that always opposes whichever way the bone is currently moving, scaled by a damping constant c), and the total force becomes F = -k*x - c*v, so a = (-k*x - c*v) / m. Step it forward with the exact same semi-implicit Euler recipe as chapter 2.5, section 6 — update velocity first, then position with the new velocity — and you have a spring bone.
c / (2 * sqrt(k * m)). Below 1 is underdamped — the spring overshoots and rings a few times before settling, which is exactly the jiggle you want for hair. Exactly 1 is critically damped — it reaches rest as fast as possible with zero overshoot (this is what Unity's built-in SmoothDamp uses internally, which comes back in section 6). Above 1 is overdamped — slower than critical, and still no overshoot. Too little damping and hair looks like it is made of jello; too much and it looks stiff and dead. The right amount sits somewhere underdamped, and you tune it by eye.using UnityEngine;
public class SpringBone : MonoBehaviour
{
public Transform bone; // the moving tip (e.g. the very end of a strand of hair)
public Transform boneRest; // a child of the PARENT marking where the bone sits if perfectly rigid
public float stiffness = 20f; // how hard the spring pulls back toward rest -- higher snaps back faster
public float damping = 3f; // how much the motion is resisted -- higher settles faster, less jiggle
public float mass = 1f;
public float boneLength = 0.3f; // distance constraint to the parent, stops the bone from stretching
Vector3 velocity;
Vector3 currentPos;
void Start()
{
currentPos = bone.position;
}
void LateUpdate()
{
Vector3 restPos = boneRest.position;
Vector3 displacement = currentPos - restPos;
Vector3 accel = (-stiffness * displacement - damping * velocity) / mass;
velocity += accel * Time.deltaTime;
currentPos += velocity * Time.deltaTime;
// distance constraint: keep the bone exactly boneLength away from its parent,
// the same idea as satisfyDistance() from chapter 2.5's Verlet cloth section
Vector3 fromParent = currentPos - bone.parent.position;
currentPos = bone.parent.position + fromParent.normalized * boneLength;
bone.position = currentPos;
}
}
The first block is the spring-damper math from the previous section, translated straight into code: compute displacement, compute acceleration from spring force plus damping force, step velocity, step position — the identical shape as chapter 2.5's spring examples, just with the extra -damping * velocity term. The last three lines are new: they nudge currentPos back onto a sphere of radius boneLength around the parent's actual position, exactly like the satisfyDistance function from chapter 2.5's cloth section, just written for a single point-to-parent link instead of two free points. Without this step, a stiff-enough spring or a big-enough Time.deltaTime could stretch the bone away from its parent by an unbounded amount; with it, the bone always stays a fixed length away, however displaced it gets.
To see the spring settle in isolation, freeze the parent for a moment (so restPos stops moving) and watch just the displacement x and velocity v, using k = 20, c = 3, m = 1, dt = 0.1 (again, a bigger dt than a real frame, purely so the table stays short), starting at x = 1 after the rest position suddenly jumped one unit away:
// step x v
// 0 1.0000 0.0000
// 1 0.8000 -2.0000
// 2 0.5000 -3.0000
// 3 0.1900 -3.1000
// 4 -0.0650 -2.5500
// 5 -0.2305 -1.6550
// 6 -0.3003 -0.6975
Watch x: it crosses zero somewhere around step 4, and by step 6 it has overshot to about -0.30 — past its rest position — before the spring force (now pointing the other way) starts hauling it back. That overshoot is not a bug; it is the entire point. A bone that stopped dead exactly at x = 0 the instant it arrived would look robotic. The swing-past-and-settle is what reads as hair, not a rigid rod, on screen.
A single spring bone gets you a jiggling earring. A strand of hair, a ponytail, or a cape needs several bones in a row, each one's rest position depending on where the previous bone in the chain actually ended up — not on the animated skeleton's original position, but on the spring-solved position from this same frame.
using UnityEngine;
[System.Serializable]
public class SpringBoneLink
{
public Transform bone; // the tip this link controls
public Transform restPose; // marks the bone's rigidly-animated position each frame
public float boneLength = 0.2f;
}
public class SpringBoneChain : MonoBehaviour
{
public SpringBoneLink[] links; // MUST be ordered ROOT FIRST, tip last
public float stiffness = 200f;
public float damping = 12f;
public float mass = 1f;
Vector3[] velocities;
Vector3[] positions;
void Start()
{
velocities = new Vector3[links.Length];
positions = new Vector3[links.Length];
for (int i = 0; i < links.Length; i++)
positions[i] = links[i].bone.position;
}
void LateUpdate()
{
// this loop MUST run root-to-tip, in array order, so link i+1 reads link i's
// freshly-solved position from THIS frame, not its stale position from last frame
for (int i = 0; i < links.Length; i++)
{
Vector3 restPos = links[i].restPose.position;
Vector3 displacement = positions[i] - restPos;
Vector3 accel = (-stiffness * displacement - damping * velocities[i]) / mass;
velocities[i] += accel * Time.deltaTime;
positions[i] += velocities[i] * Time.deltaTime;
Transform parentBone = (i == 0) ? transform : links[i - 1].bone;
Vector3 fromParent = positions[i] - parentBone.position;
positions[i] = parentBone.position + fromParent.normalized * links[i].boneLength;
links[i].bone.position = positions[i];
}
}
}
Each link is a plain copy of section 4's single spring bone, run in a loop. The one detail that actually matters is the comment: this for loop must process the root link first and the tip last, in the same frame, so that when link 2 reads links[1].bone (its parent bone), it is reading a position that this very frame's physics step already updated — not a leftover from the previous frame.
GetComponentsInChildren<SpringBone>() and updating them in whatever order that search happens to return. Unity's search order is not something you should rely on for correctness — if a child bone's script runs before its parent's in the same frame, it reads a one-frame-stale parent position, and the whole chain looks subtly disconnected or jittery, especially on fast head turns. Configure the order explicitly, root to tip, as one ordered array (like SpringBoneChain above), instead of trusting automatic discovery.A walk cycle clip is authored assuming flat ground. The moment a character walks onto a curb, a slope, or a staircase, a foot either sinks into the step or floats above it — the animation has no idea the ground height changed. The fix follows the same shape as chapter 9.3's IK, applied procedurally every frame: cast a ray straight down from each foot's animated position, and move that foot's IK target to wherever the ray actually hits.
using UnityEngine;
public class FootIK : MonoBehaviour
{
public Animator animator;
public LayerMask groundMask;
public float raycastHeight = 0.3f; // start the ray this far above the animated foot position
public float maxStepDown = 0.5f; // how far below the animated position still counts as "ground"
public float footRadius = 0.05f; // lifts the foot slightly off the exact hit point
void OnAnimatorIK(int layerIndex)
{
AdjustFoot(AvatarIKGoal.LeftFoot);
AdjustFoot(AvatarIKGoal.RightFoot);
}
void AdjustFoot(AvatarIKGoal foot)
{
Vector3 animatedPos = animator.GetIKPosition(foot);
Ray ray = new Ray(animatedPos + Vector3.up * raycastHeight, Vector3.down);
if (Physics.Raycast(ray, out RaycastHit hit, raycastHeight + maxStepDown, groundMask))
{
Vector3 targetPos = hit.point + Vector3.up * footRadius;
animator.SetIKPositionWeight(foot, 1f);
animator.SetIKPosition(foot, targetPos);
Quaternion groundRotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
animator.SetIKRotationWeight(foot, 1f);
animator.SetIKRotation(foot, groundRotation * animator.GetIKRotation(foot));
}
}
}
OnAnimatorIK is a callback Unity runs automatically right after it evaluates the base animation, but before LateUpdate — exactly the pipeline slot built for humanoid IK adjustments. animator.GetIKPosition(foot) reads where the animation currently has that foot; the raycast starts a little above it and fires straight down. On a hit, SetIKPosition moves the foot to the hit point (raised slightly by footRadius so it does not clip into the ground), and SetIKRotation tilts the foot to match the ground normal (the direction pointing straight out of the surface) using Quaternion.FromToRotation to build a rotation from "flat" to "aligned with this slope", multiplied onto the foot's existing rotation so it keeps facing the right way while tilting.
OnAnimatorIK correctly and then wondering why it never fires. It only runs if the Animator Controller's base layer has the IK Pass checkbox enabled (in the Layers panel of the Animator window) — it is off by default, and there is no error or warning if you forget it. The method just silently never gets called.The hips also need to react, or a step-up on one foot alone looks like the leg is stretching. Track a smoothed hip offset, driven by whichever foot is lower:
using UnityEngine;
public class HipOffset : MonoBehaviour
{
public Transform hips;
public float smoothTime = 0.08f;
float currentOffset;
float velocity;
// call this once you know each foot's own ground offset (e.g. from FootIK above)
public void ApplyHipOffset(float leftFootOffset, float rightFootOffset)
{
float targetOffset = Mathf.Min(leftFootOffset, rightFootOffset);
currentOffset = Mathf.SmoothDamp(currentOffset, targetOffset, ref velocity, smoothTime);
hips.position += Vector3.up * currentOffset;
}
}
// left foot ray hits a curb: +0.15 above the animated ground height
// right foot ray hits a dip: -0.05 below the animated ground height
// targetOffset = the LOWER of the two, so the higher foot never looks like it is floating
// targetOffset = min(+0.15, -0.05) = -0.05
//
// SmoothDamp is a critically damped spring under the hood -- the exact formula from
// section 3, just with damping set to critical: c = 2 * sqrt(k * m). Rolling that by
// hand (k = 100, m = 1, c = 20, dt = 0.05) on currentOffset gives:
//
// frame currentOffset
// 0 0.0000
// 1 -0.0125
// 2 -0.0219
// 3 -0.0289
// 4 -0.0342
// 5 -0.0381 -- closing in on -0.05, smoothly, with NO overshoot past it
Notice the contrast with section 4's hair spring, which was deliberately underdamped so it would overshoot and jiggle. Here, overshoot would mean the hips visibly bob up past the target height before settling — wrong for something that is supposed to read as "planted", not "springy". Same formula, opposite damping ratio, because the two jobs want opposite feel.
Nothing an animator authors can predict the exact direction, force, and body position of every possible death. The standard fix is a ragdoll: give every major bone a Rigidbody, a Collider, and a joint connecting it to its parent bone with angular limits — the same clamp idea as section 2's look-at cone, just enforced by the physics engine instead of a script. The deep mechanics of rigid bodies, colliders, and joints belong to the physics chapters (10.1, Rigid Bodies & Collision, and 10.3, Ragdolls, Cloth & Soft Bodies); this section covers the animation side of the handoff — how and when you flip the switch.
using UnityEngine;
public class RagdollToggle : MonoBehaviour
{
Animator animator;
Rigidbody[] ragdollBodies;
Collider[] ragdollColliders;
void Awake()
{
animator = GetComponent<Animator>();
ragdollBodies = GetComponentsInChildren<Rigidbody>();
ragdollColliders = GetComponentsInChildren<Collider>();
SetRagdollActive(false); // start animated: physics off, ragdoll colliders off
}
public void Die(Vector3 hitForce, Vector3 hitPoint, Rigidbody hitBone)
{
SetRagdollActive(true);
hitBone.AddForceAtPosition(hitForce, hitPoint, ForceMode.Impulse);
}
public void SetRagdollActive(bool active)
{
animator.enabled = !active; // Animator OFF once physics takes over
foreach (Rigidbody rb in ragdollBodies)
rb.isKinematic = !active; // isKinematic true = "the Animator moves this, not physics"
foreach (Collider col in ragdollColliders)
col.enabled = active;
}
}
While the character is animated, every ragdoll Rigidbody is kinematic (isKinematic = true — a flag meaning physics forces do nothing to this object; something else, here the Animator, fully controls its position), and every ragdoll Collider is disabled, so the limbs neither get pushed around by physics nor collide with anything while the animation is fully in charge. Die() flips both: the Animator switches off, every Rigidbody becomes non-kinematic so gravity and collisions can move it, colliders turn on, and one call to AddForceAtPosition with ForceMode.Impulse (an instant change in velocity, scaled by mass — chapter 10.1 covers the other force modes) gives the hit bone a sudden kick in the direction of the shot, so the character does not just crumple in place but visibly reacts to how it died.
A character that survives a knockdown should not snap instantly from a limp physics pose back into a standing animation — the pop is jarring. The usual fix: freeze exactly where the ragdoll ended up, snap the Animator back on and jump straight into a get-up clip, then blend every bone from the frozen ragdoll pose toward the live animation over a short window, shrinking the ragdoll's influence to zero.
using UnityEngine;
using System.Collections;
public class RagdollGetUp : MonoBehaviour
{
public Animator animator;
public Transform hips;
public string getUpFrontClip = "GetUpFront";
public string getUpBackClip = "GetUpBack";
public float blendTime = 0.25f;
Transform[] allBones;
void Awake()
{
allBones = GetComponentsInChildren<Transform>();
}
public void StandUp()
{
bool faceDown = Vector3.Dot(hips.up, Vector3.up) < 0f;
StartCoroutine(BlendToAnimation(faceDown ? getUpFrontClip : getUpBackClip));
}
IEnumerator BlendToAnimation(string clipName)
{
// 1. remember exactly where physics left every bone
Vector3[] ragdollPos = new Vector3[allBones.Length];
Quaternion[] ragdollRot = new Quaternion[allBones.Length];
for (int i = 0; i < allBones.Length; i++)
{
ragdollPos[i] = allBones[i].position;
ragdollRot[i] = allBones[i].rotation;
}
GetComponent<RagdollToggle>().SetRagdollActive(false); // physics off, Animator back on
animator.Play(clipName, 0, 0f);
animator.Update(0f); // force the Animator to pose the skeleton RIGHT NOW, this frame
float t = 0f;
while (t < blendTime)
{
t += Time.deltaTime;
float lerp = t / blendTime;
for (int i = 0; i < allBones.Length; i++)
{
allBones[i].position = Vector3.Lerp(ragdollPos[i], allBones[i].position, lerp);
allBones[i].rotation = Quaternion.Slerp(ragdollRot[i], allBones[i].rotation, lerp);
}
yield return null;
}
}
}
StandUp() picks between two get-up clips using Vector3.Dot(hips.up, Vector3.up) — the dot product (chapter 2.1) between the hips' current up direction and world up; negative means the hips are pointing more toward the ground than the sky, i.e. the character landed face down. BlendToAnimation first records every bone's world position and rotation exactly as physics left them, then turns the ragdoll off and forces the Animator to evaluate the get-up clip's first frame immediately with animator.Update(0f) (normally you would just wait for the next frame, but that would leave one frame where the skeleton is in an undefined in-between state). The loop then blends every bone from the recorded ragdollPos/ragdollRot toward wherever the Animator currently has it, with lerp climbing from 0 to 1 — at lerp = 0 the pose is still the frozen ragdoll, at lerp = 1 it is fully the live animation.
A worked trace, tracking just the hips' height (y) as the get-up clip lifts the character, with blendTime = 0.25 and steps of dt = 0.05:
// ragdollPos.y = 0.20 (lying on the ground)
// say the get-up clip, sampled fresh each frame, moves hips.y like this:
// frame 1 (t=0.05): animatedY = 0.24 lerp = 0.05/0.25 = 0.20 -> blended.y = 0.20+(0.24-0.20)*0.20 = 0.208
// frame 2 (t=0.10): animatedY = 0.31 lerp = 0.40 -> blended.y = 0.20+(0.31-0.20)*0.40 = 0.244
// frame 3 (t=0.15): animatedY = 0.40 lerp = 0.60 -> blended.y = 0.20+(0.40-0.20)*0.60 = 0.320
// frame 4 (t=0.20): animatedY = 0.55 lerp = 0.80 -> blended.y = 0.20+(0.55-0.20)*0.80 = 0.480
// frame 5 (t=0.25): animatedY = 0.70 lerp = 1.00 -> blended.y = 0.70 -- fully on the animation
// the hips rise smoothly, even though the RAW animated pose alone would have popped
// straight from 0.20 to 0.24 the instant the clip started
LateUpdate instead of a bare coroutine (guaranteeing it always runs after animation evaluation, same ordering rule as section 1) and often blends one compact additive pose instead of every single bone individually — but the underlying idea is identical to what is here.Every technique in this chapter plugs into one specific point in Unity's per-frame pipeline. Stacking them correctly is what makes a character with a walk cycle, aiming, jiggling hair, and foot IK all work together instead of fighting each other.
The word "additive" is doing two different jobs across this chapter, worth telling apart. Unity's Animator Controller supports layers with their Blend Mode set to Additive: an animator authors a clip as a difference from a reference pose (often the clip's own first frame), and Unity adds that difference on top of whatever the base layer produced — good for things like a breathing loop or a weapon-aim offset that an animator can hand-author once and have apply on top of any base movement. Everything else in this chapter is script-driven procedural addition: instead of an authored delta clip, code computes the delta every frame from live data (a target's position, a raycast hit, a spring's current displacement) that no animator could have baked in advance. Both are "adding a delta on top of a base pose" — they just differ in whether a human or a formula decided what that delta is.
The practical rule: authored animation is the default. Reach for procedural motion specifically for the parts of a character that must react to something the animator genuinely cannot know in advance — and once you reach for it, budget the CPU cost the same way you would budget any other per-character system, because unlike a clip, it does not get cheaper just because nobody is looking at it (that is what LOD-style scaling, the subject of Exercise 3, is for).
c / (2 * sqrt(k * m)); below 1 is underdamped (overshoots and rings), exactly 1 is critically damped (fastest approach, no overshoot), above 1 is overdamped.LateUpdate or OnAnimatorIK.LateUpdate, used for humanoid IK adjustments like foot placement; requires the "IK Pass" checkbox enabled on the Animator layer.k = 8, damping c = 2, mass m = 1, and dt = 0.25. Its rest position just jumped, leaving it at displacement x = 2 with v = 0. Using semi-implicit Euler (section 4's recipe: v += a*dt then x += v*dt, with a = (-k*x - c*v) / m), compute x and v by hand at steps 0, 1, 2, and 3. At which step does the bone first cross past its rest position?step 0: x=2.0000 v=0.0000
step 1: x=1.0000 v=-4.0000
step 2: x=0.0000 v=-4.0000
step 3: x=-0.5000 v=-2.0000
Step 0 to 1: a = -8*2 - 2*0 = -16, so v = 0 + (-16*0.25) = -4, and x = 2 + (-4*0.25) = 1. Step 1 to 2: a = -8*1 - 2*(-4) = -8 + 8 = 0 (spring pull and damping exactly cancel for an instant), so v stays at -4, and x = 1 + (-4*0.25) = 0. Step 2 to 3: a = -8*0 - 2*(-4) = 8, so v = -4 + (8*0.25) = -2, and x = 0 + (-2*0.25) = -0.5.
The bone lands exactly on its rest position (x = 0) at step 2, but it is still moving fast (v = -4), so it does not stop there — by step 3 it has swung past to x = -0.5. That overshoot is the jiggle: without enough damping to cancel the velocity in time, the spring always coasts a little past center before the (now reversed) spring force starts hauling it back.
100 degrees apart. The look-at cone limit is maxConeAngle = 60 degrees, the head currently faces exactly along spine forward (0 degrees off center), and it turns at turnSpeed = 300 degrees/second. Using dt = 0.1 (section 2's convention), trace the head's angle for frames 0 through 3. After how many frames does the head reach the clamp, and at what angle does it stop?clampedAngle = min(100, 60) = 60 -- target is well outside the cone, clamp applies
maxStep = 300 * 0.1 = 30 degrees per frame
frame 0: currentAngle = 0 (diff to clamp = 60)
frame 1: currentAngle = 30 (moved the full 30-degree step)
frame 2: currentAngle = 60 (moved the full 30-degree step, exactly reaches the clamp)
frame 3: currentAngle = 60 (diff = 0, nothing left to move)
The head reaches the clamp at frame 2, stopping at exactly 60 degrees off spine forward — the cone edge — not at the target's true 100 degrees. It stays pinned at 60 degrees from then on, unless the target moves closer to the cone or the character's body turns to bring the target back inside it.
Any three of these (or a reasonable variation) are solid answers:
dt accordingly). A little extra lag on background hair is invisible; the CPU savings are not.weight to 0 and skip the RotateTowards/Slerp work for that character entirely.The common thread: procedural motion's whole selling point is reacting correctly to things an animator could not predict, but a background NPC the player will glance at for half a second does not need that correctness — spend the CPU budget on the characters the player is actually looking at.
Authored clips, procedural code, and full physics are not three competing choices — they are three tools that sit on the same character at the same time. A base walk cycle from 9.1 and 9.2 carries the everyday motion; a clamped look-at and a chain of spring bones ride on top of it every frame, reacting to things no clip could predict; and when the character dies, physics takes over completely until a blended get-up hands control back to animation. Knowing which tool a given piece of motion actually needs — and what each one costs once you have two hundred characters on screen instead of one — is most of what this chapter set out to teach.