FSM для C#

В общем-то, на просторах Гитхаба можно найти 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 диаграммы и отрендерить её на сайте или на этом, по-моему у них один движок:

FSM 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

Нет комментариев

    Ваш комментарий