В общем-то, на просторах Гитхаба можно найти 100 и 1 вариант FSM, но у моей реализация есть свои «особенности» 😅 Сначала, краткое описание для тех кто не в курсе:
Машина состояний (конечный автомат) — это математическая модель, которая представляет систему, способную находиться только в одном из конечного числа состояний в каждый момент времени. Она переходит из одного состояния в другое в ответ на входные данные или события, при этом каждое состояние описывает определенное поведение или функцию системы.
И сразу тестовый пример из демки:
using Shardy.FSM;
/// <summary>
/// Test FSM
/// </summary>
void Test() {
_fsm = FSM<State, Action>.Builder(State.Standing)
.State(State.Standing)
.OnEnter((data) => {
Debug.Log("on enter standing");
})
.To(State.Sitting).On(Action.Down)
.To(State.Jumping).On(Action.Space).If(s => _isConditionActive)
.State(State.Sitting)
.To(State.Lying).On(Action.Down)
.To(State.Standing).On(Action.Up)
.State(State.Lying)
.To(State.Sitting).On(Action.Up)
.State(State.Jumping)
.Note("some help message here")
.OnExit((data) => {
Debug.Log("on exit jumping");
})
.To(State.Standing).On(Action.Down)
.Build();
_fsm.Trigger(Action.Down);
}
Для перехода на стейт надо активировать триггер(ы):
fsm.Trigger(Action.Down);
fsm.Trigger(Action.Down);
В таком случае получится такой результат:
initial is standing
on exit standing
on enter sitting
on exit sitting
on enter lying
Ещё подсмотрел, как сгенерить описание для UML диаграммы и отрендерить её на сайте или на этом, по-моему у них один движок:
Чтобы сделать диаграмму более «интуитивной», вместо слова state используется ключевое слово agent, этом случае можно рисовать разные линии.
@startuml
skin rose
title TestFSM
left to right direction
agent Standing
agent Sitting
agent Lying
agent Jumping
note left of Jumping
some help message here
end note
Start --> Standing
Standing --> Sitting : Down
Standing ~~> Jumping : Space
Sitting --> Lying : Down
Sitting --> Standing : Up
Lying --> Sitting : Up
Jumping --> Standing : Down
@enduml
Ещё к каждому переходу можно добавлять условия, (на картинке выше их нет) об этом подробнее в документации.
Если у перехода между состояниями есть условие(я), то линия будет рисоваться пунктирной, а если нет триггера – то с крестиком на конце. Актуально когда состояний и переходов много, можно сгенерить диаграмму и посмотреть нет ли косяков.
Вертикальная форма записи это необязательное условие 😅
И тут возникает вопрос: зачем в разработке машина состояний?
Если совсем просто — это способ сказать объекту:
Сейчас ты в таком-то состоянии, и можешь делать только это. А потом — переключись в другое состояние и делай другое.
Примеры
Персонаж игрока
Состояния: стоит, идет, прыгает, атакует.
Когда игрок нажимает «прыжок», машина состояний переключается с «идёт» на «прыгает», и только тогда проигрывается анимация прыжка и отключается движение по земле.
Противник
Состояния: патрулирует → заметил игрока → следует → атакует.
Без машины состояний враг мог бы «пытаться делать всё сразу», а это приводит к багам и странному поведению.
Карточные игры
Состояния: ожидает, ходит, бьётся, берёт.
На каждом состоянии отображается разные элементы управления, UI, запускается таймер и прочее.
В общем, использование машины состояний практически всегда упрощает код, делает поведение более предсказуемым, в процесс разработки код проще отлаживать и проще расширять.
Все исходники доступны на Github
Нет комментариев