关于 Unity Animation
Unity Animation 是面向 Unity 开发者的动画实现指导技能,涵盖代码与 Animator 集成、IK、Root Motion、Timeline 和 Playables API。它提供将动画与游戏逻辑同步及使用高级动画系统的代码模式。
主要功能
- 01集成代码与 Animator Controller
- 02使用 Animation Events 同步逻辑
- 03配置 Animator Layers 与 Blend Trees
- 04应用 IK 和 Root Motion
- 05使用 Timeline 与 Playables API
适用场景
- 01实现角色移动状态与 Blend Tree 联动
- 02在动画关键帧触发攻击或脚步逻辑
- 03为角色配置 IK、Root Motion 或局部身体动画
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ juliankerignard/ unity-skills --skill unity-animation安装后在 Claude Code 中输入/unity-animation确认已加载。
附带文件references/animation-advanced.md
详细介绍
Unity 动画
此技能的作用
指导在 Unity 中实现高级动画。涵盖代码与 Animator 的集成、IK、Root Motion、Animation Rigging、Timeline、Playables API 和高级混合树。
此技能不止介绍 Animator Controller 的简单配置,还提供可用于生产环境的代码模式,用于通过代码驱动动画、将游戏逻辑与关键帧同步,以及利用高级系统(IK、Playables API、Timeline)。
前提条件
- Animator Controller 已配置基本状态和过渡
- Animation clips 已导入(根据情况使用 Humanoid 或 Generic 骨架)
- (可选) 用于 IK 的 Animation Rigging 包(
com.unity.animation.rigging) - (可选) 用于过场动画的 Timeline 包(
com.unity.timeline)
快速开始
- 根据需求选择合适的动画系统(见决策树)
- 配置 Animator Controller(状态、过渡、参数)
- 与代码集成(缓存哈希、参数、事件)
- 使用 IK、高级混合、层进行优化
决策树
Quel systeme d'animation ?
|
+-- Animation simple (porte, plateforme, UI) ?
| --> Animation component simple (legacy) ou Animator avec 1-2 states
|
+-- Personnage joueur ou ennemi avec etats (idle, run, attack) ?
| --> Animator Controller + State Machine + code integration
|
+-- Animation cinematique / cutscene ?
| --> Timeline (PlayableDirector + tracks)
|
+-- Melange dynamique d'animations a runtime ?
| --> Playables API (mixer custom)
|
+-- IK (personnage regarde/attrape un objet) ?
| --> Animation Rigging package (Rig Builder + constraints)
|
+-- Besoin de performance extreme (milliers d'entites) ?
--> DOTS Animation (voir /unity-dots)
分步指南
步骤 1:设置 Animator 并与代码集成
[RequireComponent(typeof(Animator))]
public class CharacterAnimator : MonoBehaviour
{
private Animator animator;
// TOUJOURS cacher les hash (evite les string lookups chaque frame)
private static readonly int SpeedHash = Animator.StringToHash("Speed");
private static readonly int IsGroundedHash = Animator.StringToHash("IsGrounded");
private static readonly int JumpTrigger = Animator.StringToHash("Jump");
private static readonly int AttackTrigger = Animator.StringToHash("Attack");
private void Awake() => animator = GetComponent<Animator>();
public void SetSpeed(float speed) => animator.SetFloat(SpeedHash, speed);
public void SetGrounded(bool grounded) => animator.SetBool(IsGroundedHash, grounded);
public void TriggerJump() => animator.SetTrigger(JumpTrigger);
public void TriggerAttack() => animator.SetTrigger(AttackTrigger);
}
步骤 2:动画事件
// Appele depuis un clip d'animation a un keyframe precis
public void OnFootstep()
{
audioService.PlaySFX(footstepClip);
}
public void OnAttackHit()
{
// Activer hitbox au bon moment de l'animation
hitbox.SetActive(true);
}
public void OnAttackEnd()
{
hitbox.SetActive(false);
}
步骤 3:StateMachineBehaviour(附加到状态的逻辑)
public class AttackState : StateMachineBehaviour
{
public override void OnStateEnter(
Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
animator.GetComponent<CombatSystem>().OnAttackStart();
}
public override void OnStateExit(
Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
animator.GetComponent<CombatSystem>().OnAttackEnd();
}
}
步骤 4:Animator 层(部分身体动画)
// Controle dynamique du poids d'un layer (ex: upper body attack)
private static readonly int UpperBodyLayer = 1;
public void EnableUpperBodyOverride(float weight)
{
animator.SetLayerWeight(UpperBodyLayer, weight);
}
- Layer 0:基础层(完整的移动动画)
- Layer 1:上半身覆盖(攻击、交互),使用 Avatar Mask
- Layer 2:叠加(呼吸、受击反应)
步骤 5:用于移动的混合树
在 Animator Controller 中:
- 创建一个 2D Freeform Directional 混合树
- 参数:
MoveX(float)、MoveY(float) - 动作:待机(0,0)、向前行走(0,0.5)、向前奔跑(0,1)、向左侧移(-1,0)、向右侧移(1,0)
private static readonly int MoveXHash = Animator.StringToHash("MoveX");
private static readonly int MoveYHash = Animator.StringToHash("MoveY");
public void SetMovement(Vector2 input)
{
// Smooth damp pour eviter les changements brusques
animator.SetFloat(MoveXHash, input.x, 0.1f, Time.deltaTime);
animator.SetFloat(MoveYHash, input.y, 0.1f, Time.deltaTime);
}
严格规则
始终:
- 使用
Animator.StringToHash缓存哈希(不要在 Update 中使用字符串) - 使用 Animation Events 同步逻辑与动画(不要手动计时)
- 使用 Animator 层实现部分身体动画(上半身攻击 + 下半身奔跑)
- 在混合树中使用带阻尼的
SetFloat,实现平滑过渡 - 在
OnDestroy()中销毁PlayableGraph,避免内存泄漏
绝不:
- 在 Update 中调用
GetComponent<Animator>()——在 Awake 中缓存引用 - 使用
transition duration = 0,除非是纯逻辑状态机(会导致画面跳变) - 启用 Root Motion 时手动修改
transform.position - 无故使用
Play()或CrossFade()——优先使用 Animator Controller 的过渡
优先选择:
- 使用 2D Freeform 混合树实现方向性移动
- 使用子状态机组织复杂的 Animator Controller
- 使用 Override Controller,以不同动画片段复用状态机
- 对于新项目,优先使用 Animation Rigging,而非
OnAnimatorIK()
相关技能
/unity-code-gen——生成 StateMachineBehaviour 和 Animator 样板代码/proto——使用简单动画快速制作原型/unity-dots——使用 DOTS 动画驱动数千个实体/2d——使用 Sprite Library 和 Sprite Swap 实现 2D 动画
故障排查
| 问题 | 解决方案 |
|---|---|
| 动画不播放 | 检查状态是否可达(已连接过渡),参数是否设置正确 |
| 角色滑动/漂浮 | Root Motion 配置错误——检查 Animator 上的 "Apply Root Motion" 和动画片段的曲线 |
| Blend Tree 卡顿 | 检查动画片段的步频(foot cycle)是否一致。调整 threshold |
| IK 不起作用 | 检查 Animator Controller 中该 layer 是否启用了 "IK Pass" |
| Animation Event 未调用 | 方法必须为 public,位于与 Animator 相同的 GameObject 上,并使用正确的签名 |
| 过渡被阻止 | 检查过渡条件;如果需要立即过渡,请禁用 "Has Exit Time" |
| Layer override 不起作用 | 检查该 layer 的 Avatar Mask,以及 weight 是否 > 0 |
| PlayableGraph 泄漏 | 始终在 OnDestroy() 中调用 graph.Destroy() |
评论
支持 Markdown