Лучший способ реализовать большой конечный автомат?

В основном у меня есть конечный автомат, который управляет атаками персонажей, с таймингами, основанными на длине анимации.

Итак, например:

Я начинаю с состояния по умолчанию, и если игрок нажимает кнопку атаки, он начинает атаку, переключая состояние и устанавливая таймер на основе длины атаки. Государственная машина становится более сложной, но когда я рассматриваю перезарядные атаки, которые могут быть отменены, атаки, которые могут перемещаться в разные состояния в зависимости от того, что они попали, а также каждое государство имеет уникальные способы борьбы с атакуемым персонажем. На данный момент у меня есть большие операторы переключения. Я думал о полиморфизме, но для этого потребуется новый класс для каждого состояния, для которого существует много (например, атака, атака и финишная атака требуют отдельных состояний).

Оператор 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.

Ответ 4

Со своей стороны я использую stateforge для HFSM (http://www.stateforge.com/). Параллельное состояние, Иерархический подход и наблюдатель могут решить довольно много сложного случая.

Ответ 5

Я не могу сказать, что я когда-либо использовал его, но есть http://commons.apache.org/scxml/. Это также не так сложно писать вручную, используя подход, основанный на таблицах, предложенный другими.