U
章 02角色与战斗9 课 — 点击打开
03 / 09 · 10 分钟
角色与战斗

Animator 状态机

到目前为止的项目

Assets/

Scenes/

Arena.unity

Knight

Scripts/

PlayerMovement.csCharacterStats.csPlayerAnimator.cs

Animations/

AC_KnightLocomotion blend tree

你已经有的这一课新增

Knight 现在能用 PlayerMovement 动了,但还保持着 T-pose 硬邦邦地滑来滑去。这一课给它装上腿。

你的代码从不直接播放 clip。它只设置参数;Animator 决定这对应哪个 clip,并在它们之间混合。

动态图解

看着左边的角色把右边亮起的那个状态演出来 —— 而底部那三个参数,就是你的代码唯一碰得到的东西。

搭建 Controller

  1. 1

    在 Project 里:Create▸Animation▸Animator Controller,命名为 AC_Knight。

  2. 2

    选中 Knight,添加 Animator 组件,把 AC_Knight 拖进 Controller,把模型的 Avatar 拖进 Avatar。

  3. 3

    打开 Window▸Animation▸Animator 窗口,把 Idle clip 拖进去 —— 它就变成了橙色的默认状态。

  4. 4

    在 Parameters tab 里添加:Speed(Float)、IsGrounded(Bool)、Attack(Trigger)。

Blend Tree 好过把走/跑拆成单独状态

  1. 1

    在 Animator 里右键 → Create State → From New Blend Tree。命名为 Locomotion,并设为默认。

  2. 2

    双击它。把 Blend parameter 设为 Speed,然后加三个 motion:Idle 在 0、Walk 在 2、Run 在 6。

  3. 3

    现在一个 float 就能让 idle → walk → run 平滑交叉淡化。移动部分完全不需要过渡箭头。

用代码驱动它

PlayerAnimator.cs — on the Knight, next to PlayerMovement
using UnityEngine;

[RequireComponent(typeof(Animator))]
public class PlayerAnimator : MonoBehaviour
{
    private Animator anim;
    private CharacterController cc;

    // hashes are faster than strings
    private static readonly int SpeedHash    = Animator.StringToHash("Speed");
    private static readonly int GroundedHash = Animator.StringToHash("IsGrounded");
    private static readonly int AttackHash   = Animator.StringToHash("Attack");

    void Awake()
    {
        anim = GetComponent<Animator>();
        cc = GetComponent<CharacterController>();
    }

    void Update()
    {
        // horizontal speed only — vertical fall must not look like running
        Vector3 flat = new Vector3(cc.velocity.x, 0f, cc.velocity.z);

        anim.SetFloat(SpeedHash, flat.magnitude, 0.1f, Time.deltaTime); // 0.1f = damping
        anim.SetBool(GroundedHash, cc.isGrounded);

        if (Input.GetMouseButtonDown(0))
            anim.SetTrigger(AttackHash);
    }
}
  • SetFloat 的第三、第四个参数是 damp 时间和 delta time:值是缓变而不是跳变,所以 blend 不会突跳。

  • Trigger 是一个被消费后自动复位的 bool —— 用在 Attack 这种一次性动作上正合适。

  • StringToHash 在 static 字段里调一次就好:每帧都拿字符串去查是白费力气。

修好九成卡顿的过渡设置

  • 凡是需要响应玩家的过渡(攻击、跳跃),取消勾选 Has Exit Time。必须播完的动画则保留勾选。

  • Transition Duration 0.1–0.2 秒手感最跟手。超过 0.3 秒,角色就像喝醉了一样。

  • Interruption Source → Current State 让新的攻击能切入上一个攻击,而不是排队等待。

Animation Events —— 在挥出的那一刻命中

伤害不该在按键那一刻结算,而该在剑真正挥出去那一刻。Animation Events 能在你选的 clip 帧上调用一个方法。

C# 脚本
// on the same GameObject as the Animator
public void OnAttackHit()      // pick this name in the clip's Events row
{
    Debug.Log("hit frame");
    // damage the enemies in range
}