Menu
CoddyTech

状态机

本课属于 Coddy Unity 学习旅程的“Unity 游戏架构”单元。第 37 课,共 64 课。

Beacon 的 SetState 方法在所有具有状态的对象中都会相同:炮塔、敌人、门。可以把这段代码放在一个独立的类中,也就是状态机里,由它保存当前状态并切换状态。每个对象都创建一个状态机并调用它,而不是重复编写代码:

public class StateMachine
{
    public IState Current { get; private set; }

    public void ChangeState(IState next)
    {
        Current?.Exit();
        Current = next;
        Current.Enter();
    }

    public void Tick()
    {
        Current?.Tick();
    }
}

public class Beacon : MonoBehaviour
{
    public Renderer lamp;
    private StateMachine machine = new StateMachine();
    private IState blink;
    private IState steady;

    void Start()
    {
        blink = new BlinkState(lamp);
        steady = new SteadyState();
        machine.ChangeState(steady);
    }

    void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame) machine.ChangeState(machine.Current == steady ? blink : steady);
        machine.Tick();
    }
}

第一次调用 ChangeState 时没有要退出的状态:Current 是 null,?. 会跳过对 Exit 的调用。第 1 单元提醒过,?. 无法识别 Unity 的假 null;这指的是已销毁的 Unity 对象,而这些状态是普通的 C# 对象,所以这里使用 ?. 是安全的。Current 有一个 private set,因此没有脚本能绕过 ChangeState 及其 Exit 和 Enter 来切换状态。

只要两个状态会修改同一个东西,ChangeState 中的顺序就很重要。Exit 会撤销旧状态所做的更改,然后 Enter 设置新状态,因此最终由新状态决定结果。如果 DarkState 的 Enter 关闭了灯,并且在 BlinkState 的 Exit 之前运行,那么 Exit 就会在黑暗状态下重新打开灯。StateMachine 也是一个普通类:灯塔、炮塔和敌人会分别用 new 创建自己的状态机,并在各自的 Update 中调用它的 Tick。

challenge icon

挑战

中等

使用状态机运行玩家的移动状态。

  1. 在 StateMachine.cs 中,编写 public class StateMachine,包含 public IState Current { get; private set; },并编写一个会对当前状态进行 Tick 的 Tick()。
  2. ChangeState(IState next) 会忽略当前状态;否则,调用 Exit,设置 Current,再调用 Enter。
  3. Player:Start 创建 machine、walk 和 sneak,并进入行走状态;Update 在按住空格键时进入潜行状态,否则进入行走状态,然后调用 Tick。

按“开始”并按住空格键:Sneak: enter。然后按“检查”。

亲手试试

// StateMachine:Current、ChangeState 和 Tick

Unity 游戏架构中的所有课

自己练习: Unity 在线编辑器