Menu
CoddyTech

Конечный автомат

Часть раздела Архитектура игр в Unity путешествия по Unity на Coddy. Урок 29 из 50.

Метод SetState у Beacon был бы одинаковым для любого объекта с состояниями: турели, врага, двери. Этот код может находиться в отдельном классе — конечном автомате, который хранит текущее состояние и меняет его. Каждый объект создаёт отдельный автомат и вызывает его вместо того, чтобы повторять код:

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 предупреждалось, что ?. не распознаёт фальшивый null в Unity; это относится к уничтоженным объектам Unity, а состояния — обычные объекты C#, поэтому здесь ?. безопасен. У Current есть private set, поэтому ни один скрипт не может сменить состояние, не вызвав ChangeState и его Exit и Enter.

Порядок внутри ChangeState важен, когда два состояния обращаются к одному и тому же объекту. Exit отменяет изменения старого состояния, а затем Enter настраивает новое, поэтому последнее слово остаётся за новым состоянием. Если бы Enter у DarkState выключил лампу и выполнился до Exit у BlinkState, этот Exit снова включил бы лампу в тёмном состоянии. StateMachine тоже является обычным классом: маяк, турель и враг каждый создают собственный экземпляр с помощью new и вызывают его Tick из своего Update.

challenge icon

Задание

Средне

Удерживай пробел, чтобы красться. WalkState и SneakState уже готовы в PlayerStates.cs: их методы Enter и Exit записывают в лог происходящее (Walk: enter, Sneak: exit...), а метод Tick вызывает Move() игрока. Напиши машину, которая ими управляет.

  • В StateMachine.cs напиши обычный класс StateMachine с публичным свойством Current, значение которого может задавать только машина, методом ChangeState(IState next), который вызывает Exit старого состояния, а затем Enter нового, и методом Tick(), который вызывает Tick текущего состояния.
  • Игрок запрашивает своё состояние каждый кадр, поэтому вызов ChangeState с уже текущим состоянием ничего не делает: ни Exit, ни Enter.
  • В Player.cs добавь поле типа StateMachine с именем machine. В Start создай машину и оба состояния (каждому передай игрока) и начни с ходьбы. В Update переключайся на скрытное передвижение, пока удерживается пробел, а в противном случае — на ходьбу, затем вызывай Tick машины.

Нажми Play, походи, удерживая пробел, продолжай идти, а затем нажми Check.

Попробуйте сами

// StateMachine: Current, ChangeState и Tick
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Архитектура игр в Unity