Конечный автомат
Часть раздела Архитектура игр в 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.
Задание
СреднеУдерживай пробел, чтобы красться. 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
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Архитектура игр в Unity
1Компоненты, а не иерархии
Небольшие компонентыБазовый скрипт врагаАбстрактные компонентыСовместная работа компонентовПовторение: модульная турель4Сериализованные данные
Сериализуемые классыСписки в InspectorПеречисления в InspectorПовторение: таблица лута7Конечные автоматы
Состояния как классыКонечный автоматПереходыЛогика враговПовторение: состояния двери2Интерфейсы в Unity
IDamageableИнтерфейсы и TryGetComponentGetComponentsПовторение: клавиша взаимодействия5ScriptableObjects
Ресурсы данныхХарактеристики оружияОбщие настройкиВолны как данныеПовторение: база предметов