跳到主要内容

动画系统

Unity 的动画系统(Mecanim)是一个强大而灵活的系统,支持从简单的 Sprite 动画到复杂的人形角色动画。

动画系统概述

动画系统组件:
├── Animation Clip - 动画片段,存储关键帧数据
├── Animator Controller - 动画控制器,管理状态和过渡
├── Animator - 组件,执行动画控制
└── Avatar(人形动画)- 骨骼映射配置

Animation Clip(动画片段)

创建动画

方法1:在 Unity 中制作

  1. 选中对象,打开 Animation 窗口(Ctrl+6)
  2. 点击 Create 创建新动画
  3. 点击 Add Property 添加要动画的属性
  4. 在时间轴上添加关键帧

方法2:导入外部动画

  • FBX 文件中的动画
  • 专门的动画文件(.anim)

动画属性类型

属性类型说明
Transform位置、旋转、缩放
Sprite RendererSprite 切换(2D 动画)
Material材质属性变化
Light光照属性变化
自定义组件任何可序列化属性

2D Sprite 动画

public class SpriteAnimationHelper : MonoBehaviour
{
// 方法1:使用 Animation 组件(Legacy)
public Animation animation;

// 方法2:使用 Animator 组件(推荐)
public Animator animator;

void PlayLegacyAnimation()
{
animation.Play("Walk");
}

void PlayMecanimAnimation()
{
animator.Play("Walk");
}
}

Animator Controller

Animator Controller 是动画状态机的核心。

创建 Animator Controller

  1. Assets > Create > Animator Controller
  2. 将 Controller 拖到对象的 Animator 组件上

Animator 窗口

Window > Animation > Animator

Animator 窗口布局:
┌─────────────────────────────────────┐
│ Parameters │ │
│ - Speed │ Animation States │
│ - IsGrounded│ │
│ - Attack │ [Idle] ──► [Walk] │
│ │ │ │ │
│ Layers │ ▼ ▼ │
│ - Base │ [Jump] ◄─── [Run] │
│ - UpperBody │ │
└─────────────────────────────────────┘

动画状态(State)

状态类型说明
Empty空状态,不播放动画
Animation播放单个动画片段
Blend Tree混合树,混合多个动画
Sub-State Machine子状态机,组织复杂逻辑

过渡(Transition)

状态之间的切换条件:

过渡设置:
├── Has Exit Time - 是否等待当前动画播放完成
├── Exit Time - 退出时间(0-1)
├── Transition Duration - 过渡持续时间
├── Transition Offset - 目标动画起始偏移
└── Conditions - 过渡条件

动画参数(Parameters)

控制状态机行为的变量:

参数类型用途
Float速度、方向等连续值
Int状态索引、模式选择
Bool开关状态(是否跳跃、是否攻击)
Trigger一次性触发(攻击、受伤)
public class AnimatorParameterController : MonoBehaviour
{
public Animator animator;
public Rigidbody rb;

void Update()
{
// 设置 Float 参数
float speed = rb.velocity.magnitude;
animator.SetFloat("Speed", speed);

// 设置 Bool 参数
bool isGrounded = CheckGrounded();
animator.SetBool("IsGrounded", isGrounded);

// 设置 Trigger 参数
if (Input.GetButtonDown("Attack"))
{
animator.SetTrigger("Attack");
}

// 重置 Trigger(通常不需要手动调用)
// animator.ResetTrigger("Attack");
}
}

Blend Tree(混合树)

混合树允许根据参数平滑混合多个动画。

1D Blend Tree

基于单个参数混合:

Blend Tree (1D) - 基于 Speed 参数:
Speed = 0 ────► Idle
Speed = 2 ────► Walk
Speed = 6 ────► Run

2D Blend Tree

基于两个参数混合(如方向和速度):

Blend Tree (2D) - 基于 X 和 Z 方向:

Z = 1

Walk │ Walk
Left │ Right

X = -1 ────┼──── X = 1

Walk │ Walk
Back │ Forward

Z = -1
public class BlendTreeController : MonoBehaviour
{
public Animator animator;

void Update()
{
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");

// 设置 2D Blend Tree 参数
animator.SetFloat("X", horizontal);
animator.SetFloat("Z", vertical);
}
}

人形动画(Humanoid)

Avatar 配置

人形动画使用 Avatar 系统映射骨骼:

  1. 导入人形模型
  2. 选择模型,Rig > Animation Type > Humanoid
  3. 点击 Configure 配置骨骼映射

骨骼映射

Humanoid Avatar 骨骼结构:
├── Hips(髋部 - 根节点)
│ ├── Spine(脊柱)
│ │ ├── Chest(胸部)
│ │ │ ├── Neck(颈部)
│ │ │ │ └── Head(头部)
│ │ │ ├── Left Shoulder ──► Left Arm ──► Left Hand
│ │ │ └── Right Shoulder ──► Right Arm ──► Right Hand
│ │ └── Upper Legs ──► Lower Legs ──► Feet

动画重定向(Retargeting)

人形动画可以在不同角色间共享:

public class AnimationRetargeting : MonoBehaviour
{
public Animator animator;
public AnimationClip originalClip;

void Start()
{
// 只要 Avatar 配置正确,动画可以重定向
// 不需要额外代码,Animator 自动处理
}
}

动画事件

在动画时间轴上触发函数调用:

public class AnimationEvents : MonoBehaviour
{
// 这个方法会在动画事件点被调用
public void OnFootstep()
{
// 播放脚步声
AudioManager.Play("Footstep");
}

public void OnAttackHit()
{
// 检测攻击命中
CheckAttackCollision();
}

public void OnAnimationEnd()
{
// 动画结束回调
ResetAttackState();
}
}

添加动画事件:

  1. 在 Animation 窗口选中动画
  2. 点击 Add Event 按钮
  3. 选择要调用的函数

动画层(Layers)

动画层允许同时播放多个动画:

动画层示例:
├── Base Layer (Weight: 1)
│ └── 全身动画(Idle, Walk, Run)
├── Upper Body Layer (Weight: 1, Mask: UpperBody)
│ └── 上半身动画(Aim, Shoot)
└── Additive Layer (Weight: 0.5, Blending: Additive)
└── 附加动画(Breathing, Wounded)
public class AnimationLayers : MonoBehaviour
{
public Animator animator;

void Start()
{
// 设置层权重
animator.SetLayerWeight(1, 1f); // Upper Body 层
animator.SetLayerWeight(2, 0.5f); // Additive 层
}

void Update()
{
// 在不同层播放动画
animator.Play("Aim", 1); // 在 Upper Body 层
}
}

Inverse Kinematics(IK,反向动力学)

IK 允许根据目标位置自动计算关节角度:

public class IKController : MonoBehaviour
{
public Animator animator;
public Transform rightHandTarget;
public Transform lookAtTarget;

// 启用 IK
void OnAnimatorIK(int layerIndex)
{
// 设置右手 IK 目标
if (rightHandTarget != null)
{
animator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1f);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1f);
animator.SetIKPosition(AvatarIKGoal.RightHand, rightHandTarget.position);
animator.SetIKRotation(AvatarIKGoal.RightHand, rightHandTarget.rotation);
}

// 设置视线目标
if (lookAtTarget != null)
{
animator.SetLookAtWeight(1f);
animator.SetLookAtPosition(lookAtTarget.position);
}
}
}

启用 IK:

  1. 在 Animator 窗口选中层
  2. 勾选 IK Pass

Root Motion(根运动)

使用动画本身驱动角色移动:

public class RootMotionController : MonoBehaviour
{
public Animator animator;

void OnAnimatorMove()
{
// 应用根运动
transform.position += animator.deltaPosition;
transform.rotation *= animator.deltaRotation;
}
}

Animator 设置:

  • Apply Root Motion:勾选使用根运动
  • Update Mode:选择更新时机

Timeline 和 Animation 集成

Timeline 可以编排多个动画:

public class TimelineController : MonoBehaviour
{
public PlayableDirector director;

void PlayTimeline()
{
director.Play();
}

void PauseTimeline()
{
director.Pause();
}

void StopTimeline()
{
director.Stop();
}
}

动画性能优化

优化建议

  1. 使用 Animator Culling Mode

    • Always Animate - 始终动画
    • Cull Update Transforms - 不可见时停止更新
    • Cull Completely - 不可见时完全停止
  2. 减少骨骼数量

    • 移动端使用简化骨骼
    • 移除不必要的骨骼
  3. 使用 Animation Compression

    • 导入设置中启用压缩
    • 调整错误阈值
  4. 避免频繁调用 Animator 属性

    // 不好的做法
    void Update()
    {
    animator.SetFloat("Speed", speed); // 每帧调用
    }

    // 好的做法
    void Update()
    {
    if (speed != lastSpeed)
    {
    animator.SetFloat("Speed", speed);
    lastSpeed = speed;
    }
    }

实践:完整的角色动画系统

public class CharacterAnimationSystem : MonoBehaviour
{
[Header("组件")]
public Animator animator;
public CharacterController controller;

[Header("动画参数")]
private int speedHash = Animator.StringToHash("Speed");
private int isGroundedHash = Animator.StringToHash("IsGrounded");
private int jumpHash = Animator.StringToHash("Jump");
private int attackHash = Animator.StringToHash("Attack");
private int hurtHash = Animator.StringToHash("Hurt");
private int dieHash = Animator.StringToHash("Die");

[Header("状态")]
public float moveSpeed;
public bool isGrounded;
public bool isAttacking;

void Update()
{
// 移动输入
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector3 move = new Vector3(horizontal, 0, vertical);

// 更新动画参数
moveSpeed = move.magnitude;
animator.SetFloat(speedHash, moveSpeed);

// 地面检测
isGrounded = controller.isGrounded;
animator.SetBool(isGroundedHash, isGrounded);

// 跳跃
if (Input.GetButtonDown("Jump") && isGrounded)
{
animator.SetTrigger(jumpHash);
}

// 攻击
if (Input.GetButtonDown("Fire1") && !isAttacking)
{
animator.SetTrigger(attackHash);
isAttacking = true;
}
}

// 动画事件回调
public void OnAttackStart()
{
// 启用攻击判定
EnableAttackHitbox(true);
}

public void OnAttackEnd()
{
// 禁用攻击判定
EnableAttackHitbox(false);
isAttacking = false;
}

public void OnFootstep()
{
// 播放脚步声
PlayFootstepSound();
}

void EnableAttackHitbox(bool enable)
{
// 实现攻击判定
}

void PlayFootstepSound()
{
// 播放脚步声
}
}

下一步

掌握动画系统后,你可以:

  1. 学习 UI 系统 创建游戏界面
  2. 探索 音频系统 添加音效和音乐
  3. 了解 粒子系统 制作视觉特效
  4. 学习 寻路系统 实现 AI 移动