Лучший способ реализовать большой конечный автомат?
В основном у меня есть конечный автомат, который управляет атаками персонажей, с таймингами, основанными на длине анимации.
Итак, например:
Я начинаю с состояния по умолчанию, и если игрок нажимает кнопку атаки, он начинает атаку, переключая состояние и устанавливая таймер на основе длины атаки. Государственная машина становится более сложной, но когда я рассматриваю перезарядные атаки, которые могут быть отменены, атаки, которые могут перемещаться в разные состояния в зависимости от того, что они попали, а также каждое государство имеет уникальные способы борьбы с атакуемым персонажем.
На данный момент у меня есть большие операторы переключения. Я думал о полиморфизме, но для этого потребуется новый класс для каждого состояния, для которого существует много (например, атака, атака и финишная атака требуют отдельных состояний).
Оператор switch работает, но его довольно большой и также не так легко модифицируется, как система наследования.
Любые предложения по реалистичным реализациям?
EDIT:
Это использует java.
Ответы
Ответ 1
С более крупными государственными машинами вы должны следить за феноменом "переходного взрыва". Оказывается, традиционные машины конечного состояния не предоставляют механизмов для повторного использования общих переходов во многих штатах, поэтому вы в конечном итоге повторяете слишком много, и ваша государственная машина "взрывается" (это также относится к Do-not-Repeat-Yourself ( DRY).
По этой причине я бы рекомендовал использовать иерархические машины состояний и методы реализации, которые позволяют легко сопоставлять такие государственные машины на код. Дополнительные сведения об иерархических государственных машинах см. В статье Wikipedia http://en.wikipedia.org/wiki/UML_state_machine.
Ответ 2
Рассмотрим построение конечного автомата с табличным управлением. Если вы думаете об определении конечного автомата, у вас есть в основном набор состояний с выделенным начальным состоянием, функцией перехода и (в данном случае) входным и выходным алфавитом.
Вы можете построить таблицу, индексированную текущим состоянием и вводом, и использовать указатель на функцию или класс функтора для представления того, что происходит. Эта функция должна вернуть следующее состояние. Затем вы можете построить конечный автомат как (псевдокод):
state := initial state
while(state != STOP)
state := (lookupTransition(inputs))()
где lookupTransition(inputs)
просто находит следующее состояние. Я предполагаю здесь, что функции перехода - это функции без аргументов, возвращающих состояние, поэтому lookupTransition(inputs)
должна быть функцией любого количества ресурсов, которые у вас есть, возвращая указатель на функцию возврата состояния void.
Установите это правильно, и вы можете поместить все конечные машины и поведение в одну таблицу, которую можно легко модифицировать и перекомпилировать.
(Для получения дополнительной информации выясните, как вы можете загрузить эту таблицу из файла, поэтому вам не нужно вообще перекомпилировать.)
Обновление
В Java вместо указателей функций используйте что-то вроде класса functor.
Другое обновление
Конечно, другой вариант - использовать компилятор конечного авто, например Ragel.
Ответ 3
Boost имеет совершенно прекрасный конечный автомат, единственный недостаток становится привычным для программирования шаблонов/типов
http://www.boost.org/doc/libs/1_46_1/libs/statechart/doc/index.html
Ответ 4
Со своей стороны я использую stateforge для HFSM (http://www.stateforge.com/).
Параллельное состояние, Иерархический подход и наблюдатель могут решить довольно много сложного случая.
Ответ 5
Я не могу сказать, что я когда-либо использовал его, но есть http://commons.apache.org/scxml/. Это также не так сложно писать вручную, используя подход, основанный на таблицах, предложенный другими.