acombine.ru
← все заметки

Конечный автомат вместо десяти флагов

Каждая моя игра на раннем этапе выглядела одинаково: горстка булевых флагов, которые где-то включаются, а где-то выключаются. isAnimating, waitingForInput, isPaused, levelEnding. Пока их три — терпимо. Когда стало восемь, я поймал баг, из-за которого игрок мог сделать ход прямо во время анимации проигрыша, и понял, что дальше так нельзя.

Проблема флагов в том, что они допускают невозможные сочетания. Восемь булевых значений — это 256 комбинаций, а осмысленных из них штук шесть. Остальные 250 — это те самые «а как ты вообще сюда попал» баги.

Одна переменная состояния

Конечный автомат (state machine) переворачивает подход: вместо десяти «да/нет» одна переменная, у которой всего несколько разрешённых значений. В любой момент игра ровно в одном состоянии.

const State = {
  IDLE:      "idle",       // ждём ввода игрока
  ANIMATING: "animating",  // проигрываем анимацию хода
  RESOLVING: "resolving",  // считаем последствия
  LEVEL_END: "level_end",  // экран победы/поражения
};

let state = State.IDLE;

Ввод игрока принимается ровно в одном состоянии:

function onTap(x, y) {
  if (state !== State.IDLE) return;   // всё, одна строка
  state = State.ANIMATING;
  playMove(x, y, () => { state = State.RESOLVING; });
}

Та самая проверка if (state !== State.IDLE) return закрывает целый класс багов, на которые раньше уходили вечера. Нельзя нажать во время анимации — потому что состояние уже не IDLE.

Переходы в одном месте

Второе, что даёт автомат, — все правила переходов собраны в одной функции, а не размазаны по обработчикам. Я обычно завожу явную таблицу «из какого состояния в какое можно», и любой недопустимый переход — это громкая ошибка в логе, а не тихо испорченный кадр.

Если переход состояния произошёл не там, где положено, я хочу узнать об этом сразу и в консоли, а не через неделю от игрока.

Когда это перебор

Честно: для совсем простой аркады на одну кнопку автомат может быть избыточен — там и двух флагов хватит. Я достаю его, когда состояний становится больше четырёх или когда появляются анимации, во время которых ввод надо блокировать. Для пошаговых игр это происходит почти сразу.

С тех пор я начинаю новую игру не с графики, а с трёх строк: список состояний, стартовое состояние, функция перехода. Дальше всё нанизывается на этот скелет, и «невозможные» баги просто перестают возникать.

← все заметки