在游戏开发中,动画是赋予角色和物体生命的关键技术。Unity提供了两套核心动画系统:Animation和Animator。这两套系统看似相似,实则有着完全不同的设计理念和应用场景。
Animation组件是Unity早期的动画解决方案,它直接操作游戏对象的Transform或材质属性,适合简单的、线性的动画播放需求。比如一个旋转的门、上下移动的平台,或者颜色渐变的UI元素。它的优势在于轻量、直接,所有动画数据都存储在.anim文件中。
而Animator则是更强大的状态机驱动系统,内置了复杂的逻辑控制能力。它通过Animator Controller资源来管理多个动画状态及其转换关系,支持参数控制、混合树、动画层等高级功能。现代3D角色动画几乎都建立在Animator系统之上。
重要提示:从Unity 2018开始,官方推荐使用Animator而非旧版Animation组件进行角色动画开发。但了解Animation组件仍有助于理解基础动画原理。
在Project窗口右键选择Create > Animation Clip,命名为"DoorOpen"。双击打开动画窗口(Window > Animation > Animation),此时可以看到一个空的时间轴。
选中场景中的门对象,点击Add Property按钮,展开Transform选择Rotation。这将在时间轴0:00处自动创建关键帧。将时间轴拖到1:00位置,修改Y轴旋转值为90,系统会自动生成新的关键帧。
此时运行游戏,门会从0度旋转到90度。但你会发现动画只播放一次就停止了。要解决这个问题,我们需要修改动画片段的循环设置:
在Animation窗口中切换到Curves视图,可以看到旋转值随时间变化的曲线。默认是线性插值,这会导致运动看起来机械不自然。
尝试调整曲线为缓入缓出(Ease-In-Out):
对于更复杂的动画,比如角色跳跃,你可能需要分别处理位置Y轴和缩放Y轴的曲线:
动画事件允许在特定时间点触发游戏逻辑。比如我们想在门完全打开时播放音效:
csharp复制public void OnDoorOpenComplete() {
AudioSource.PlayClipAtPoint(openSound, transform.position);
}
创建一个新的Animator Controller(Create > Animator Controller),双击打开Animator窗口。你会看到默认存在的三个状态:
右键空白处选择Create State > Empty,命名为"Idle"。这是角色的默认待机状态。再创建"Walk"和"Run"状态。
设置状态过渡:
对于复杂的移动动画,混合树(Blend Tree)是更好的选择。比如处理8方向行走:
在角色控制脚本中更新参数:
csharp复制void Update() {
Vector2 input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
animator.SetFloat("DirectionX", input.x);
animator.SetFloat("DirectionY", input.y);
animator.SetFloat("Speed", input.magnitude);
}
动画层允许叠加多个动画状态。比如在基础移动层之上添加表情层:
csharp复制// 受伤时加强表情层权重
animator.SetLayerWeight(1, isHurt ? 0.8f : 0.3f);
在Project窗口选中动画片段,Inspector中关键优化选项:
对于人形动画,启用Muscle Definition:
Unity的人形动画系统允许在不同比例的角色间共享动画:
csharp复制animator.MatchTarget(
targetPosition,
targetRotation,
AvatarTarget.Root,
new MatchTargetWeightMask(Vector3.one, 1f),
0.1f, 0.5f);
动画事件虽然方便,但过度使用会导致性能问题:
AnimatorStateScript允许在特定状态执行自定义逻辑:
示例:攻击状态自动检测命中
csharp复制public class AttackStateBehaviour : StateMachineBehaviour {
override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
if (stateInfo.normalizedTime > 0.3f && stateInfo.normalizedTime < 0.4f) {
// 只在攻击动作特定帧检测碰撞
CheckHit();
}
}
}
除了常规参数,Animator还可以读取动画曲线值:
csharp复制float power = animator.GetFloat("AttackPower");
对于过场动画,可以结合Timeline工具:
混合使用Animator和Timeline:
csharp复制// 暂停Animator控制
animator.enabled = false;
// 播放Timeline动画
director.Play();
// 结束后恢复控制
StartCoroutine(ResumeAnimatorAfterTimeline());
症状:状态切换时模型短暂回到T-Pose
解决方案:
问题:角色移动速度不稳定
修复步骤:
csharp复制void OnAnimatorMove() {
if (animator.applyRootMotion) {
transform.position += animator.deltaPosition;
transform.rotation *= animator.deltaRotation;
}
}
改善过渡效果的技巧:
针对移动设备的特殊配置:
减少内存占用的方法:
实用调试技巧:
关键性能指标:
优化策略: