Unity provides two distinct animation systems: the modern Mecanim system (simply called the Animation system) and the Legacy system. The Mecanim system, driven by the Animator component, Animator Controller, and Animation Clips, is the recommended choice for most projects, especially those requiring complex blending and state machines. The Legacy system is still available for simple tasks like basic UI animations.
Workflow Overview
The core workflow involves Animation Clips, which are linear recordings of property changes (position, rotation, scale, etc.). These clips are organized in an Animator Controller, a state machine that manages which clip plays and how transitions and blends occur. For humanoid characters, an Avatar maps the generic skeleton to Unity's muscle definitions, enabling retargeting and inverse kinematics. An Animator component ties these elements together on a GameObject.
Rotating Objects in Animation
When animating rotation, Unity interpolates between keyframes. Three interpolation modes handle the path of rotation:
- Euler Angles: Follows the literal Euler values; a 370° rotation spins more than a full circle.
- Euler Angles (Quaternion): Converts Euler angles to quaternion curves for storage. It uses slightly more memory but offers faster runtime performance.
- Quaternion: Always takes the shortest rotation path. A 365° rotation is treated as 5°.
For animations imported from external tools, Unity may resample quaternion curves to guarantee consistent rotation paths. If the default resampling causes issues, it can be disabled in the import settings.
Animation Clips
Clips can originate from two sources:
Externally Imported
- Humanoid animations
- Animations authored in 3D DCC tools
- Asset store packages
- Timelines sliced into multiple clips
Imported clip data is read‑only in the Animation window. To edit it, create a new empty clip and paste the keyframes into it.
Internally Created
Use the Animation window (Dopesheet or Curves view) to animate properties such as:
- Transform components
- Material colors, light intensity, audio volume
- Script variables (float, integer, bool, etc.)
Keyframe shortcuts:
,/.move frame by frameAlt+,/Alt+.jump to previous/next keyframeFin Curves view frames the selection
Add keyframes via context menu, the record button, or the K hotkey (adds keys for all animated properties).
Example uses: creating flickering lights, modulating audio pitch, scrolling texture offsets, particle system bursts, or driving script variables.
Humanoid Avatars
The Avatar system defines how an imported skeleton maps to Unity's humanoid template. This allows animations made for one humanoid character to drive another, enabling retargeting and shared IK setups.
Animation Events
Attach a script with a public method to the animated GameObject, then add an event at a specific keyframe. The method can accept primitive types, an object reference, or an AnimationEvent parameter.
Animator Controllers
Controllers contain states and transitions, forming a visual state machine.
Animation Parameters
Four parameter types drive transitions:
- Float, Integer, Bool, Trigger
Set them viaSetFloat,SetInteger,SetBool,SetTrigger,ResetTrigger.
Example script:
void Update() {
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
bool shoot = Input.GetButtonDown("Fire1");
characterAnimator.SetFloat("SpeedX", horizontal);
characterAnimator.SetFloat("SpeedZ", vertical);
characterAnimator.SetBool("Shooting", shoot);
}
void OnCollisionEnter(Collision col) {
if (col.gameObject.CompareTag("Enemy")) {
characterAnimator.SetTrigger("Defeated");
}
}
State Machine Behaviours
Attach scripts derived from StateMachineBehaviour to states. They receive lifecycle callbacks (OnStateEnter, OnStateUpdate, OnStateExit, etc.). Multiple states can share the same script instance by applying the [SharedBetweenAnimators] attribute, reducing memory overhead.
Example:
using UnityEngine;
[SharedBetweenAnimators]
public class SpellCastBehaviour : StateMachineBehaviour
{
public GameObject spellPrefab;
public float radius;
public float force;
private GameObject instance;
public override void OnStateEnter(Animator anim, AnimatorStateInfo stateInfo, int layer)
{
instance = Instantiate(spellPrefab, anim.rootPosition, Quaternion.identity);
Rigidbody rb = instance.GetComponent<Rigidbody>();
rb.AddExplosionForce(force, anim.rootPosition, radius, 3f);
}
public override void OnStateExit(Animator anim, AnimatorStateInfo stateInfo, int layer)
{
Destroy(instance);
}
}
Optimizing with StringToHash
Instead of passing parameter names as strings every frame, use Animator.StringToHash("parameterName") once and store the integer hash. Integer comparisons are faster and use less memory.
Sub‑State Machines
Group related states (e.g., locomotion states) into a hexagonal sub‑state machine. External transitions can target a specific state inside the sub‑machine.
Animation Layers
Layers blend different parts of the body. Each layer can have an Avatar Mask to control which body parts are afffected. Blend types:
- Override: replaces lower layers.
- Additive: adds the layer's animation on top, controlled by weight. The additive clip must animate the same properties as the base layers.
A gear icon on the layer header opens settings; an 'M' indicates a mask is applied.
Layer Syncing
A sync layer mirrors the state machine structure of a source layer. The individual Animation Clips can be replaced, but the state layout and transitions are identical. The Timing option determines which layer's clip duration is used for synchronization. An 'S' mark denotes a synced layer.
Solo and Mute
For debugging, you can Mute a transition (disabling it) or Solo one or more transitions (only those transitions are allowed). Mute overrides Solo. Solo transitions appear green, muted ones red.
Target Matching
Target matching adjusts a character's position and rotation to reach a specific point during an animation, such as jumping onto a platform. Use Animator.MatchTarget with normalized start and end times from the clip. Only one match can be active at a time; applyRootMotion must be enabled.
Example:
using UnityEngine;
[RequireComponent(typeof(Animator))]
public class JumpTargetController : MonoBehaviour
{
private Animator anim;
public Transform landingSpot;
void Start() => anim = GetComponent<Animator>();
void Update()
{
if (Input.GetButton("Fire1"))
{
anim.MatchTarget(
landingSpot.position,
landingSpot.rotation,
AvatarTarget.LeftFoot,
new MatchTargetWeightMask(Vector3.one, 1f),
0.141f,
0.78f
);
}
}
}
A helper script to handle overlapping requests:
using UnityEngine;
public class MatchTargetHandler : MonoBehaviour
{
public void PerformMatch(Vector3 targetPos, Quaternion targetRot, AvatarTarget bodyPart, MatchTargetWeightMask weightMask, float startNormalized, float endNormalized)
{
var anim = GetComponent<Animator>();
if (anim.isMatchingTarget) return;
float normTime = Mathf.Repeat(anim.GetCurrentAnimatorStateInfo(0).normalizedTime, 1f);
if (normTime > endNormalized) return;
anim.MatchTarget(targetPos, targetRot, bodyPart, weightMask, startNormalized, endNormalized);
}
}
Inverse Kinematics (IK)
IK calculates joint rotations from a desired end‑effector position. Its commonly used to make feet adapt to uneven ground or to grip objects. Enable the IK pass on a layer, then implement OnAnimatorIK. Set weights and positions using methods such as SetIKPositionWeight and SetIKPosition.
Example:
using UnityEngine;
public class IKHandler : MonoBehaviour
{
private Animator anim;
public Transform targetObject;
public Transform lookTarget;
public bool ikEnabled;
void Start() => anim = GetComponent<Animator>();
void OnAnimatorIK(int layer)
{
if (!anim) return;
if (ikEnabled)
{
if (lookTarget)
{
anim.SetLookAtWeight(1);
anim.SetLookAtPosition(lookTarget.position);
}
if (targetObject)
{
anim.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
anim.SetIKRotationWeight(AvatarIKGoal.RightHand, 1);
anim.SetIKPosition(AvatarIKGoal.RightHand, targetObject.position);
anim.SetIKRotation(AvatarIKGoal.RightHand, targetObject.rotation);
}
}
else
{
anim.SetIKPositionWeight(AvatarIKGoal.RightHand, 0);
anim.SetIKRotationWeight(AvatarIKGoal.RightHand, 0);
anim.SetLookAtWeight(0);
}
}
}
Root Motion
The Body Transform (center of mass, usually the chest) is stored in world space within the clip. The Root Transform is a projection of the Body Transform onto the ground plane and is recalculated each frame. It drives the actual movement of the GameObject.
Clip import settings provide three sections to control how the Body Transform maps to the Root Transform: Root Transform Rotation, Root Transform Position (Y), and Root Transform Position (XZ).
- Bake Into Pose: When enabled, the corresponding aspect does not modify the GameObject's transform; instead it stays in the animated pose. For example, a walk cycle with baked Y position will not change the character's height.
- Based Upon: Select the reference (e.g., Body Orientation, Feet, Original) used to derive the root offset.
- Offset: Additional manual offset.
Scripting Root Motion for In‑Place Animations
To move a character via script using an in‑place animation:
- On the imported clip, create a curve (e.g., "MoveSpeed") that holds the forward speed.
- Add a float parameter of the same name to the Animator Controller.
- Implement
OnAnimatorMoveto read that parameter and apply translation manually.
using UnityEngine;
[RequireComponent(typeof(Animator))]
public class CustomRootMotion : MonoBehaviour
{
void OnAnimatorMove()
{
Animator anim = GetComponent<Animator>();
if (anim)
{
Vector3 pos = transform.position;
pos.z += anim.GetFloat("MoveSpeed") * Time.deltaTime;
transform.position = pos;
}
}
}