3.7 动画系统 (Animation) (Animator, AnimationClip, State Machine) 3.7 动画系统 (Animation) 在 Unity3D 中,动画系统是构建生动、引人入胜的游戏和互动体验的关键组成部分。它赋予静态模型以生命,通过控制模型的外观、运动和交互,极大地增强了游戏的沉浸感和表现力。Unity 的动画系统功能强大且灵活,允许开发者创建从简单的角色移动到复杂的生物行为等各种动画效果。 3.7.
3.7 动画系统 (Animation)
在 Unity3D 中,动画系统是构建生动、引人入胜的游戏和互动体验的关键组成部分。它赋予静态模型以生命,通过控制模型的外观、运动和交互,极大地增强了游戏的沉浸感和表现力。Unity 的动画系统功能强大且灵活,允许开发者创建从简单的角色移动到复杂的生物行为等各种动画效果。
3.7.1 动画系统的核心概念
Unity 的动画系统是基于组件化的架构设计的,主要围绕以下三个核心概念展开:
AnimationClip (动画剪辑): AnimationClip 是动画数据的载体,它包含了在一段时间内,游戏对象属性(如位置、旋转、缩放、材质属性、骨骼权重等)的变化信息。AnimationClip 可以通过 Unity 编辑器手动创建,也可以从外部 3D 建模软件(如 Blender, Maya, 3ds Max)中导入。
Animator (动画器): Animator 组件是动画系统的核心控制器,它负责驱动和管理游戏对象上的动画播放。Animator 组件依附于带有 Animator Controller 资源的 GameObject 上,通过状态机 (State Machine) 定义动画的播放逻辑和状态转换,并根据参数控制动画的切换和混合。
State Machine (状态机): State Machine 是 Animator Controller 的可视化编辑器,用于定义动画状态以及状态之间的转换规则。状态机由多个状态 (State) 和状态之间的过渡 (Transition) 组成。每个状态通常关联一个或多个 AnimationClip,表示游戏对象在特定状态下的动画行为。过渡定义了状态之间切换的条件和方式,例如,从“Idle”(待机)状态过渡到“Walk”(行走)状态,可能需要一个“Speed”参数大于某个阈值。
这三个组件相互协作,共同构建了 Unity 强大的动画系统:AnimationClip 提供动画数据,Animator 负责动画控制,State Machine 定义动画逻辑。
3.7.2 AnimationClip:动画的基石
AnimationClip 是动画系统的基础,它存储了动画的实际数据。AnimationClip 可以控制游戏对象及其子对象在时间轴上的各种属性。
AnimationClip 的关键组成部分:
Curves (曲线): AnimationClip 的核心是曲线数据。每条曲线代表一个属性在时间轴上的变化。例如,一条曲线可以控制一个物体的 X 轴位置,另一条曲线可以控制其 Y 轴旋转。Unity 支持多种类型的曲线,包括:
Position Curves (位置曲线): 控制 Transform 组件的 localPosition 属性。
Rotation Curves (旋转曲线): 控制 Transform 组件的 localRotation 属性 (通常使用 Quaternion 表示)。
Scale Curves (缩放曲线): 控制 Transform 组件的 localScale 属性。
Euler Angles Curves (欧拉角曲线): 控制 Transform 组件的 localEulerAngles 属性 (不推荐直接使用,容易产生 Gimbal Lock 问题)。
Float Curves (浮点数曲线): 控制任何浮点数类型的属性,例如材质的颜色强度、Shader 的 Uniform 变量等。
Color Curves (颜色曲线): 控制颜色类型的属性,例如材质的颜色。
Object Reference Curves (对象引用曲线): 控制对象引用类型的属性,例如激活/禁用 GameObject。
Keyframes (关键帧): 曲线是由一系列关键帧定义的。每个关键帧包含一个时间和对应的值。Unity 会在关键帧之间进行插值,平滑地生成动画的中间帧。
Wrap Mode (循环模式): 定义动画播放完毕后的行为。常见的循环模式包括:
Default: 使用 Animator 组件或状态机的默认循环模式。
Once: 动画播放一次后停止。
Loop: 动画循环播放。
PingPong: 动画来回循环播放。
ClampForever: 动画播放到最后一帧后保持最后一帧的状态。
AnimationClip 的创建和使用:
AnimationClip 可以通过以下几种方式创建:
在 Unity 编辑器中创建 (Animation Window):
打开 Animation Window (Window -> Animation -> Animation)。
选择要添加动画的 GameObject。
点击 "Create" 按钮创建一个新的 AnimationClip 资源。
在 Animation Window 中,点击 "Add Property" 添加要控制的属性,并添加关键帧来编辑曲线。
从外部 3D 建模软件导入:
在 3D 建模软件中创建动画,并导出为 Unity 支持的格式 (如 .fbx, .dae)。
将模型文件导入 Unity 项目。
Unity 会自动解析模型文件中的动画数据,并生成 AnimationClip 资源。
通过脚本代码动态创建:
AnimationClip 类在脚本中动态创建动画剪辑。这在程序化动画、运行时动画生成等场景中非常有用。代码实践:动态创建 AnimationClip
以下代码示例演示了如何通过脚本动态创建一个简单的旋转动画 AnimationClip:
using UnityEngine; public class CreateAnimationClip : MonoBehaviour { void Start() { // 1. 创建一个新的 AnimationClip 对象 AnimationClip clip = new AnimationClip(); clip.name = "RotationAnimation"; // 设置动画剪辑的名称 clip.wrapMode = WrapMode.Loop; // 设置循环模式为循环播放 // 2. 创建一条旋转曲线 (围绕 Y 轴旋转) AnimationCurve curve = new AnimationCurve(); // 添加关键帧:0 秒时旋转 0 度 curve.AddKey(0f, 0f); // 添加关键帧:1 秒时旋转 360 度 curve.AddKey(1f, 360f); // 3. 将旋转曲线添加到 AnimationClip,指定要控制的属性路径 // "localEulerAngles.y" 表示控制 Transform 组件的 localEulerAngles.y 属性 clip.SetCurve("", typeof(Transform), "localEulerAngles.y", curve); // 4. 获取 Animator 组件 (确保 GameObject 上有 Animator 组件) Animator animator = GetComponent<Animator>(); if (animator == null) { animator = gameObject.AddComponent<Animator>(); } // 5. 创建一个 RuntimeAnimatorController (如果还没有) if (animator.runtimeAnimatorController == null) { AnimatorController controller = AnimatorController.CreateAnimatorControllerAtPath("Assets/New Animator Controller.controller"); animator.runtimeAnimatorController = controller; } // 6. 将 AnimationClip 添加到 AnimatorController 中 UnityEditor.Animations.AnimatorController animController = animator.runtimeAnimatorController as UnityEditor.Animations.AnimatorController; if (animController != null) { UnityEditor.Animations.AnimatorStateMachine rootStateMachine = animController.layers[0].stateMachine; // 获取默认层级的状态机 UnityEditor.Animations.AnimatorState animState = new UnityEditor.Animations.AnimatorState(); animState.name = "RotateState"; animState.motion = clip; // 将 AnimationClip 关联到状态 rootStateMachine.AddState(animState, new Vector3(100, 0, 0)); // 将状态添加到状态机中 (可指定位置) // 设置默认状态为 RotateState (可选) // rootStateMachine.defaultState = animState; } Debug.Log("AnimationClip created and added to Animator Controller."); } }
代码详解:
AnimationClip clip = new AnimationClip();: 创建一个新的 AnimationClip 对象,用于存储动画数据。
clip.name = "RotationAnimation";: 设置 AnimationClip 的名称,方便在编辑器中识别。
clip.wrapMode = WrapMode.Loop;: 设置动画的循环模式为 Loop,使其循环播放。
AnimationCurve curve = new AnimationCurve();: 创建一个 AnimationCurve 对象,用于定义旋转角度随时间变化的曲线。
curve.AddKey(0f, 0f); curve.AddKey(1f, 360f);: 向曲线中添加两个关键帧。
curve.AddKey(0f, 0f); 表示在 0 秒时,旋转角度为 0 度。
curve.AddKey(1f, 360f); 表示在 1 秒时,旋转角度为 360 度。
clip.SetCurve("", typeof(Transform), "localEulerAngles.y", curve);: 将曲线添加到 AnimationClip 中。
第一个参数 "" 表示相对路径为空,因为我们直接控制当前 GameObject 的 Transform 组件。
typeof(Transform) 指定要控制的组件类型为 Transform。
"localEulerAngles.y" 指定要控制的属性路径为 Transform 组件的 localEulerAngles.y 属性 (Y 轴旋转)。
最后一个参数 curve 是我们创建的旋转曲线。
获取 Animator 组件和 AnimatorController: 代码获取或创建 Animator 组件和 AnimatorController,确保动画可以被播放。
UnityEditor.Animations.AnimatorController animController = animator.runtimeAnimatorController as UnityEditor.Animations.AnimatorController;: 获取 Animator 组件关联的 AnimatorController。
UnityEditor.Animations.AnimatorStateMachine rootStateMachine = animController.layers[0].stateMachine;: 获取 AnimatorController 的默认层级的状态机 (通常是 Layer 0)。
创建 AnimatorState 并关联 AnimationClip: 创建一个新的 AnimatorState,并将其 motion 属性设置为我们动态创建的 clip (AnimationClip)。
rootStateMachine.AddState(animState, new Vector3(100, 0, 0));: 将 AnimatorState 添加到状态机中。
设置默认状态 (可选): 可以设置状态机的默认状态,使动画在运行时自动播放。
运行代码后,将此脚本添加到场景中的一个 GameObject 上 (例如 Cube),确保 GameObject 上有 Animator 组件 (如果没有,脚本会自动添加)。运行游戏,您会看到 Cube 会围绕 Y 轴循环旋转。
3.7.3 Animator 组件:动画的控制器
Animator 组件是 Unity 动画系统的核心控制中心,它负责管理和驱动 GameObject 上的动画播放。Animator 组件依附于 GameObject 上,并需要关联一个 Animator Controller 资源。
Animator 组件的主要功能:
动画播放和控制: Animator 组件负责播放关联的 Animator Controller 中定义的动画状态。可以通过脚本控制动画的播放、暂停、停止、速度等。
状态机驱动: Animator 组件根据 Animator Controller 中定义的状态机逻辑,自动管理动画状态的切换和混合。
参数驱动动画: Animator 组件允许定义参数 (Parameters),这些参数可以被脚本动态修改,并用于驱动状态机中的状态转换和动画混合。
动画层级和混合: Animator 组件支持多层级的动画控制,允许在不同的层级上混合不同的动画,例如,角色上半身的动画和下半身的动画可以分别控制和混合。
Avatar Mask (Avatar 遮罩): Animator 组件可以使用 Avatar Mask 来限制动画对骨骼的影响范围,例如,只让上半身动画影响上半身骨骼,而不影响下半身骨骼。
IK (Inverse Kinematics, 逆向运动学): Animator 组件支持 IK 功能,允许通过控制末端骨骼的位置,反向计算并驱动整个骨骼链的运动,常用于角色手臂抓取物体、脚部贴地等效果。
事件 (Animation Events): AnimationClip 中可以添加动画事件,在动画播放到特定时间点时触发回调函数,用于在动画播放过程中执行脚本逻辑,例如播放音效、触发特效、执行游戏逻辑等。
Animator 组件在 Inspector 面板中的主要属性:
Controller (Animator Controller): 关联的 Animator Controller 资源,定义了动画状态机和动画逻辑。
Avatar: 关联的 Avatar 资源,定义了骨骼映射关系,用于动画重定向 (Animation Retargeting)。
Apply Root Motion: 是否应用根运动。如果启用,动画的根骨骼运动会影响 GameObject 的 Transform 组件。
Update Mode: 动画更新模式,可以选择 Normal, Animate Physics, Unscaled Time 等。
Culling Mode: 动画剔除模式,用于优化性能,可以选择基于距离或完全剔除。
Play Automatically: 是否在启动时自动播放默认动画状态。
Parameters: Animator Controller 中定义的参数列表,可以在 Inspector 中查看和修改参数值 (运行时)。
Layers: Animator Controller 中定义的动画层级列表。
通过脚本控制 Animator 组件:
可以使用 GetComponent<Animator>() 获取 GameObject 上的 Animator 组件,并通过其提供的 API 来控制动画。
常用的 Animator API:
animator.Play(stateName): 立即播放指定名称的状态。
animator.CrossFade(stateName, transitionDuration): 平滑过渡到指定名称的状态,并指定过渡时间。
animator.SetBool(parameterName, value): 设置布尔类型参数的值。
animator.SetFloat(parameterName, value): 设置浮点数类型参数的值。
animator.SetInteger(parameterName, value): 设置整数类型参数的值。
animator.SetTrigger(parameterName): 触发 Trigger 类型参数。
animator.GetBool(parameterName) / animator.GetFloat(parameterName) / animator.GetInteger(parameterName): 获取参数的值。
animator.GetCurrentAnimatorStateInfo(layerIndex): 获取当前动画状态的信息。
animator.IsInTransition(layerIndex): 判断是否正在进行状态过渡。
animator.speed: 获取或设置动画播放速度。
animator.enabled: 启用或禁用 Animator 组件。
代码实践:通过脚本控制 Animator 参数和动画播放
以下代码示例演示了如何通过脚本控制 Animator 的布尔参数 "IsWalking" 和 Trigger 参数 "Jump",从而切换不同的动画状态:
using UnityEngine; public class AnimatorControllerScript : MonoBehaviour { Animator animator; void Start() { animator = GetComponent<Animator>(); if (animator == null) { Debug.LogError("Animator component not found!"); } } void Update() { // 控制 "IsWalking" 参数 if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.D)) { animator.SetBool("IsWalking", true); // 设置 IsWalking 为 true,触发行走动画 } else { animator.SetBool("IsWalking", false); // 设置 IsWalking 为 false,触发待机动画 } // 控制 "Jump" 参数 if (Input.GetKeyDown(KeyCode.Space)) { animator.SetTrigger("Jump"); // 触发 Jump Trigger,播放跳跃动画 } } }
代码详解:
animator = GetComponent<Animator>();: 在 Start() 函数中获取 GameObject 上的 Animator 组件。
animator.SetBool("IsWalking", true); / animator.SetBool("IsWalking", false);: 在 Update() 函数中,根据键盘输入 (WASD 键) 控制 Animator 的布尔参数 "IsWalking"。
当按下 WASD 键时,设置 "IsWalking" 为 true,Animator Controller 的状态机应该根据这个参数值,从 "Idle" 状态过渡到 "Walk" 状态,播放行走动画。
当松开 WASD 键时,设置 "IsWalking" 为 false,状态机应该从 "Walk" 状态过渡回 "Idle" 状态,播放待机动画。
animator.SetTrigger("Jump");: 当按下空格键时,触发 Animator 的 Trigger 参数 "Jump"。
运行代码后,将此脚本添加到带有 Animator 组件的 GameObject 上,并确保 Animator Controller 中定义了 "IsWalking" (Bool 类型) 和 "Jump" (Trigger 类型) 参数,以及相应的状态和过渡。您可以通过 WASD 键控制角色行走/待机动画,通过空格键触发跳跃动画。
3.7.4 状态机 (State Machine):动画的逻辑编排
状态机 (State Machine) 是 Animator Controller 的核心组成部分,它使用可视化图形界面,用于定义动画状态以及状态之间的转换规则,从而编排复杂的动画逻辑。
状态机的基本元素:
State (状态): 状态代表游戏对象在特定动画行为下的状态。每个状态通常关联一个或多个 AnimationClip,表示在该状态下播放的动画。状态可以是:
Animation State: 播放一个或多个 AnimationClip 的状态。
Blend Tree State: 混合多个 AnimationClip 的状态,根据参数值动态混合动画。
Sub-State Machine State: 嵌套子状态机的状态,用于组织更复杂的动画逻辑。
Empty State: 不播放任何动画的空状态,可以用作状态机的起始状态或中间状态。
Transition (过渡): 过渡定义了从一个状态到另一个状态的切换规则。每个过渡包含:
Conditions (条件): 触发过渡的条件,通常基于 Animator 的参数值。条件可以是参数值比较、参数触发等。
Duration (过渡时间): 状态过渡的持续时间,控制动画混合的平滑程度。
Exit Time (退出时间): 当前状态动画播放到一定比例后才能触发过渡。
Interruption Source (中断源): 定义过渡是否可以被其他过渡中断。
Ordered Interruption (有序中断): 定义过渡中断的优先级顺序。
Transition Duration Mode (过渡时间模式): 定义过渡时间的计算方式 (Fixed Duration 或 Percent Duration)。
Has Exit Time (是否有退出时间): 是否使用退出时间作为过渡条件。
Parameter (参数): 参数是 Animator Controller 中定义的变量,用于驱动状态机中的状态转换和动画混合。参数类型包括:
Float (浮点数): 用于表示连续变化的数值,例如速度、混合权重等。
Integer (整数): 用于表示离散的数值,例如状态索引、动画片段索引等。
Bool (布尔值): 用于表示开关状态,例如是否行走、是否跳跃等。
Trigger (触发器): 用于触发一次性事件,例如跳跃、攻击等。
状态机工作原理:
状态机在运行时会根据当前状态和参数值,不断评估状态之间的过渡条件。当一个过渡的条件满足时,状态机就会从当前状态切换到目标状态,并根据过渡设置进行动画混合。
Mermaid 图表示例:简单的角色动画状态机
以下 Mermaid 图表展示了一个简单的角色动画状态机,包含 "Idle" (待机), "Walk" (行走), "Run" (跑), "Jump" (跳跃), "Fall" (下落), "Land" (落地) 状态:
图表解释:
States (状态): 图中矩形框表示状态,例如 "Idle", "Walk", "Run", "Jump", "Fall", "Land"。
Transitions (过渡): 箭头线表示状态之间的过渡,例如 "Idle --> Walk"。
Parameters (参数): 图中菱形框表示参数,例如 "IsWalking", "IsRunning", "IsJumping", "IsFalling", "OnGround"。
Transition Conditions (过渡条件): 箭头线上方的文字表示过渡条件,例如 "IsWalking == true"。
状态机逻辑描述:
从 "Idle" 状态到 "Walk" 状态的过渡条件是 "IsWalking" 参数为 true。
从 "Walk" 状态到 "Idle" 状态的过渡条件是 "IsWalking" 参数为 false。
从 "Walk" 状态到 "Run" 状态的过渡条件是 "IsRunning" 参数为 true。
从 "Run" 状态到 "Walk" 状态的过渡条件是 "IsRunning" 参数为 false。
可以直接从 "Idle" 状态过渡到 "Run" 状态,反之亦然。
从 "Idle", "Walk", "Run" 状态都可以通过触发 "IsJumping" Trigger 参数过渡到 "Jump" 状态。
从 "Jump" 状态到 "Fall" 状态的过渡条件是 "OnGround" 参数为 false (角色离开地面)。
从 "Fall" 状态到 "Land" 状态的过渡条件是 "OnGround" 参数为 true (角色回到地面)。
从 "Land" 状态自动过渡回 "Idle" 状态。
代码实践:使用参数驱动状态机
结合之前的 AnimatorControllerScript.cs 代码,以及上述状态机图表,我们可以实现一个简单的角色移动和跳跃动画控制。
在 Unity 编辑器中创建 Animator Controller 和状态机:
创建一个新的 Animator Controller 资源 (例如 "CharacterAnimatorController")。
双击打开 Animator Controller,进入状态机编辑器。
创建以下参数:
IsWalking (Bool)
IsJumping (Trigger)
导入或创建 "Idle", "Walk", "Jump" 等 AnimationClip。
创建 "Idle", "Walk", "Jump" 三个状态,并将对应的 AnimationClip 关联到状态上。
创建状态之间的过渡,并设置过渡条件 (参考上述 Mermaid 图表和状态机逻辑描述)。例如:
"Idle" -> "Walk" 过渡:Condition: IsWalking is true
"Walk" -> "Idle" 过渡:Condition: IsWalking is false
"Idle" -> "Jump" 过渡:Condition: IsJumping is Triggered
"Walk" -> "Jump" 过渡:Condition: IsJumping is Triggered
运行代码后,确保 GameObject 上挂载了 Animator 组件,并关联了创建的 "CharacterAnimatorController" 资源,以及 AnimatorControllerScript.cs 脚本。运行游戏,您可以通过 WASD 键控制角色行走/待机动画,通过空格键触发跳跃动画,状态机会根据参数值自动切换动画状态。
3.7.5 动画系统实践案例:简易角色动画控制
为了更好地理解动画系统的应用,我们来完成一个简单的角色动画控制案例:
案例目标: 创建一个简单的角色,能够根据键盘输入进行待机、行走、跳跃动画的切换。
步骤:
准备模型和动画资源:
导入一个角色模型 (例如 Unity Asset Store 中的免费角色模型)。
确保模型包含 "Idle" (待机), "Walk" (行走), "Jump" (跳跃) 等动画剪辑。如果没有现成的动画,可以使用 Mixamo 等在线动画资源网站下载,并导入到 Unity 项目中。
创建 Animator Controller 和状态机:
创建一个新的 Animator Controller 资源 (例如 "CharacterController")。
双击打开 Animator Controller。
创建以下参数:
IsWalking (Bool)
JumpTrigger (Trigger)
创建 "Idle", "Walk", "Jump" 三个状态,并将对应的 AnimationClip 关联到状态上。
创建状态之间的过渡,并设置过渡条件:
"Idle" -> "Walk": Condition: IsWalking is true, Has Exit Time: false, Transition Duration: 0.1s
"Walk" -> "Idle": Condition: IsWalking is false, Has Exit Time: false, Transition Duration: 0.1s
"Idle" -> "Jump": Condition: JumpTrigger is Triggered, Has Exit Time: false, Transition Duration: 0.2s
"Walk" -> "Jump": Condition: JumpTrigger is Triggered, Has Exit Time: false, Transition Duration: 0.2s
(可以根据需要调整过渡时间和条件)
状态机 Mermaid 图 (简化版):
graph TD
Idle --> Walk
Walk --> Idle
Idle --> Jump
Walk --> Jump
subgraph States
Idle
Walk
Jump
end
subgraph Parameters
IsWalking[IsWalking Bool]
JumpTrigger[JumpTrigger Trigger]
end
Idle -- IsWalking == true --> Walk
Walk -- IsWalking == false --> Idle
Idle -- JumpTrigger Trigger --> Jump
Walk -- JumpTrigger Trigger --> Jump