> For the complete documentation index, see [llms.txt](https://docs.bestway.com.ua/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bestway.com.ua/gem-rts-v1-ru/animacii/anymatsyonnaia_systema_yunytov/format-opisaniya-fsm.md).

# Описание FSM

Система FSM (Finite State Machine) предназначена для централизованного управления состояниями игровых объектов, обеспечивая чёткое и последовательное выполнение действий на основе различных входящих запросов от управляющих подсистем (AI, пользовательские команды, физика и другие).\
Цель FSM — разрешать конфликтующие запросы и выбирать наиболее подходящее состояние объекта для поддержания логической согласованности и плавности анимаций, а также корректной работы игровых механик.

{% hint style="info" %}
FSM не запускает анимации и не управляет логикой поведения.\
FSM используется как арбитр, который определяет допустимое следующее состояние юнита при наличии нескольких входящих запросов на изменение состояния.
{% endhint %}

**Основные функции FSM:**

* приём запросов от внешних управляющих систем;
* разрешение конфликтов между запросами;
* выбор допустимого перехода и обновление текущего состояния юнита;
* передача состояния в другие системы: анимации, поведение, скрипты и пр.

## Компоненты FSM

### Состояние

**Состояние** (**state**) представляет собой описание текущей позиции или действия юнита (например: stand, walk, fire, blown\_away).\
В каждый момент времени юнит может находиться только в одном состоянии.\
Состояния являются узлами в FSM-графе переходов между состояниями.

Каждое состояние:

* имеет уникальное имя;
* имеет как минимум один тег;
* связи между состояниями формируют возможные переключения между состояниями.

### Теги

**Тег** (**tag**) — это текстовая метка, назначаемая состоянию. Тег выполняет две функции:

* позволяет внешним системам понять, в каком логическом состоянии находится юнит;
* определяет, какой запрос должен привести к данному состоянию.\
  Пример: одно из доступных состояний с тегом fire будет выбрано в ответ на запрос fire.

### Запросы

**Запрос** (**request**)— это внешняя команда, поступающая от управляющих подсистем игры (AI, пользовательского ввода, физики и др.), которая инициирует в рамках FSM процесс перевода управляемого объекта в новое состояние.

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

Каждый запрос связан как минимум с одним состоянием FSM через одноимённый тег состояния. Наличие тега позволяет FSM идентифицировать, что данное состояние способно обработать указанный запрос при выполнении дополнительных условий перехода.

{% hint style="info" %}
Теги в состояниях могут использоваться не только для связи с запросами, но и для работы других игровых подсистем.
{% endhint %}

Результат обработки запроса зависит от:

* текущего состояния управляемого объекта;
* приоритета запроса;
* правил переходов, описанных в FSM-графе.

FSM поддерживает несколько **типов запросов**.

{% hint style="danger" %}
**Типы запросов** заданы в коде и не могут быть переопределены через конфигурационные файлы FSM.
{% endhint %}

{% hint style="info" %}
*Таблица типов запросов приведена для ознакомления, с целью целостного восприятия работы конечного автомата (FSM) в Gem RTS.*
{% endhint %}

<table><thead><tr><th width="125.6484375">Тип</th><th width="365.98046875">Описание</th><th>Пример</th></tr></thead><tbody><tr><td><code>Switch</code></td><td>Запрос этого типа вызывается в коде однократно и ожидает в очереди возможности для выполнения. После выполнения удаляется.</td><td>Команда “Лечь”</td></tr><tr><td><code>Job</code></td><td>Тип запроса, вызываемый из кода каждый квант после перевода юнита в целевое FSM-состояние. Удерживает юнита в этом состоянии, пока запрос остаётся активным. Запрос данного типа сбрасывается при отсутствии его вызова из кода дольше, чем указано в параметре <code>duration</code>, или при поступлении более приоритетного запроса.<br>При сбросе запускает запрос типа <code>Switch</code> из блока <code>exit</code> в описании FSM, если юнит всё ещё находится в целевом состоянии.</td><td>Команда “Ремонтировать”</td></tr><tr><td><code>Action</code></td><td>Запрос данного типа вызывается из кода каждый квант до возможности перехода юнита в целевое состояние; при срабатывании переводит юнита в целевое состояние для выполнения действия, после чего удаляется. Если запрос не вызывался хотя бы один квант, то он удаляется без выполнения.</td><td>Команда “Бросить гранату”</td></tr><tr><td><code>State</code></td><td>Запрос вызывается в коде однократно, после чего происходит переход в целевое состояние. Состояние сохраняется, пока не будет отменен или не придёт более приоритетный запрос.</td><td>Спрятать оружие, если оно воткнулось в стену</td></tr></tbody></table>

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

{% hint style="success" %}
[Список запросов и тегов, предопределенных в коде](/gem-rts-v1-ru/animacii/anymatsyonnaia_systema_yunytov/format-opisaniya-fsm/fsm-dlya-yunita-chelovek.md)
{% endhint %}

### Подмножества состояний

**FSM-подмножество** (**Subset**) — это компонент FSM, задающий ограничения на переходы, исключая из рассмотрения состояния, несовместимые с текущим состоянием объекта.

**Пример**\
Если юнит удерживает канистру, **FSM-подмножество** исключает состояния лежать, сидеть и ремонтировать, поскольку выполнение этих действий несовместимо с текущим состоянием объекта.

## Обработка запроса в FSM

### **Этапы процесса обработки запроса:**

* **Выбор FSM-подмножества**\
  В соответствии с текущим состоянием юнита обработчик состояний выбирает **FSM-подмножество**, которое ограничивает выборку доступных для перехода состояний.
* **Анализ приоритетов**\
  При поступлении нескольких запросов одновременно FSM анализирует их приоритеты. Запрос с наивысшим приоритетом имеет преимущество при выборе нового состояния. Равнозначные по приоритету запросы разрешаются согласно порядку поступления.
* **Проверка доступности перехода**:
  * Задан ли графом FSM переход в целевое состояние из текущего состояния.
  * Выполняются ли условия (например, заданы соответствующие теги и/или дополнительные параметры запроса).
* **Результат обработки запроса**\
  FSM выбирает ближайшее доступное по графу состояние, соответствующее тегу запроса, независимо от того, находится ли оно в прямой связи с текущим состоянием — переключение состояний может происходить через промежуточные состояния.

### Типичный сценарий работы FSM

Стартовая позиция: юнит находится в состоянии ожидания (idle).\
Начинается бой: юнит обнаруживает противника, получает приказ от AI атаковать врага и одновременно команду от игрока «окопаться». Рядом с юнитом происходит взрыв, взрывная волна достаточной силы, чтобы отбросить юнита и нанести ему урон. Запросы на изменение состояния юнита отправляются в FSM.\
Поскольку юнит не может пребывать одновременно в нескольких состояниях (окапываться, атаковать, реагировать на взрыв, лечиться или умирать), FSM разрешает этот конфликт, выбирая наиболее приоритетное и допустимое состояние.

При активации нового состояния объекта, обработчик состояний передает состояние в игровые системы, а также запускает скрипты и анимации, соответствующие этому состоянию.

При выходе объекта из состояния обработчик состояний информирует игровые системы об окончании состояния, останавливает воспроизведение соответствующих анимаций и эффектов, а также связанных с состоянием процессов и скриптов, после чего очищает ресурсы.

## Компоненты конфигурации FSM

Логика конечного автомата описывается в **.def** файле объекта с использованием блока `fsm`:

> **Формат записи**
>
> ```
> {fsm "name"
>   ...
> }
> ```
>
> где `"name"` — имя FSM.

{% hint style="info" %}
**Описание FSM** может быть вынесено в отдельный файл или каталог.

Например, в ресурсах игры **Men of War II**:

* `\properties\animal.ext` — FSM для объекта `horse`;
* `\properties\human_fsm\` — FSM для объекта `human`.
  {% endhint %}

### Список FSM-запросов

Блоки `request` в описании FSM используется для регистрации запросов.

{% hint style="danger" %}
Полное описание запросов находится в исходном коде и не доступен для переконфигурации в текстовых файлах игры.
{% endhint %}

Каждый блок соответствует одному именованному запросу и включает параметры, определяющие правила его обработки внутри FSM.

> **Структура описания запроса**
>
> ```
> {Request "name"
>     {priority N} 
>     {interrupt}
>     {duration N}
>     {exit "state_name"} 
> }
> ```

<table><thead><tr><th width="139.54296875">Параметр</th><th>Описание</th></tr></thead><tbody><tr><td><code>Request</code></td><td>Имя запроса.<br>FSM выбирает состояние, у которого тег совпадает с указанным именем.</td></tr><tr><td><code>priority</code></td><td>Определяет приоритет запроса.<br>При обработке конкурирующих запросов выбирается запрос с более высоким значением.</td></tr><tr><td><code>interrupt</code></td><td>Разрешает немедленный переход в новое состояние, принудительно завершая текущее, независимо от состояния активных анимаций.<br>Опционально.</td></tr><tr><td><code>duration</code></td><td>Задает максимальный интервал в тактах между последовательными вызовами запроса из кода; если запрос не вызывается в течение указанного времени, он автоматически отменяется.<br>Опционально, только для <strong>Job</strong>-запросов</td></tr><tr><td><code>exit</code></td><td>Задает имя состояния для автоматического перехода при отмене запроса после превышения времени ожидания, указанного в параметре <code>duration</code>.<br>Опционально, только для <strong>Job</strong>-запросов</td></tr></tbody></table>

<details>

<summary>Пример описания запроса в fsm</summary>

```
{Request "move"
    {priority 30}
    {interrupt}
    {duration 2}
    {exit "stop-move"}
}
```

Блок регистрирует запрос с именем `move`.

При обработке запросов FSM выберет этот запрос, если все остальные поступившие запросы имеют приоритет ниже `30`.

Параметр `interrupt` указывает, что запрос может немедленно прервать текущее состояние, независимо от завершения анимаций.

FSM будет искать состояние, у которого в параметре `tag` указано значение `move`, и осуществит переход в него при активации запроса.

Запрос остаётся активным до тех пор, пока он регулярно обновляется в коде. Если в течение двух тактов обновление не произошло (`duration 2`), запрос автоматически отменяется.

В случае отмены запроса FSM выполнит переход в состояние с тегом `stop-move`, как задано параметром `exit`.

</details>

### Начальное FSM-состояние

Блок параметра `Start` используется для указания имени состояния для инициализации юнита.

> **Формат описания начального fsm-состояния**
>
> ```
> {Start "state_name"}
> ```

### Список FSM-состояний

С помощью блоков параметра `state` задаются узлы графа FSM, они же FSM-состояния.\
Вложенные блоки используются для описания доступных переходов между состояниями, тегов состояний и других параметров.

> **Структура описания параметров fsm-состояний**
>
> ```
> {State "state_name"
>     {from "source_state"}
>     {fromTagged "source_tag"}
>     {transit "transit_state"}
>     {to "target_state"}
>     {tag "name"}
>     {condition "name"}
>     {split "part1" "part2"}
> }
> ```

<table><thead><tr><th width="258.82421875">Параметр</th><th>Описание</th></tr></thead><tbody><tr><td><code>State "state_name"</code></td><td>Уникальный идентификатор описываемого состояния</td></tr><tr><td><code>from "source_state"</code></td><td>Определяет исходное состояние, из которого разрешён переход в описываемое состояние.</td></tr><tr><td><code>fromTagged "source_state"</code></td><td>Разрешает переход в описываемое состояние из любого состояния, имеющего указанный тег.</td></tr><tr><td><code>transit "transit_state"</code></td><td>(Опционально) Определяет автоматический переход в следующее состояние без внешнего запроса, как только это станет возможно.</td></tr><tr><td><code>to "target_state"</code></td><td>Доступность перехода в указанное состояние из описываемого. Может быть указано несколько строк с параметром <code>to</code>, если допустимы переходы в альтернативные состояния.</td></tr><tr><td><code>tag "name"</code></td><td>Тег описываемого состояния.<br>Опционально может быть записано несколько строк с параметром tag для одного состояния.</td></tr><tr><td><code>condition "name"</code></td><td>Условие, при котором разрешён вход в это состояние, в качестве значения параметра указывается имя запроса.</td></tr><tr><td><code>split "part1" "part2"</code></td><td>Разделение объекта на полунезависимые части (например, "walk_all" "linked"). Используется для сложных переходов или действий, когда анимацию юнита можно разделить на анимации его частей: например, верхней и нижней частей туловища.</td></tr></tbody></table>

{% hint style="success" %}
Имена состояний и названия тегов записываются в двойных кавычках
{% endhint %}

<table data-card-size="large" data-view="cards"><thead><tr><th data-type="content-ref"></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td><a href="/gem-rts-v1-ru/animacii/anymatsyonnaia_systema_yunytov/format-opisaniya-fsm/fsm-dlya-yunita-chelovek.md">Список запросов и тегов, прописанных в коде</a></td><td><a href="https://2052154247-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fx2fkAZ6oKGGdByFhKPpa%2Fuploads%2FrCk1dAvSKKe04Fp9WkP4%2Fsplash_editor_3.jpg?alt=media&amp;token=a89d5e03-56d3-401d-b04b-8da366458bd9">cover picture default</a></td></tr><tr><td><a href="/gem-rts-v1-ru/animacii/anymatsyonnaia_systema_yunytov/format-opisaniya-fsm/fsm-sostoyaniya-obekta-human.md">Fsm-состояния объекта human</a></td><td><a href="https://2052154247-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fx2fkAZ6oKGGdByFhKPpa%2Fuploads%2F0EwYEOKQOp9G6lNjKbRt%2F%D0%9E%D1%82%D0%BB%D0%B0%D0%B4%D0%BA%D0%B0%20%D0%B0%D0%BD%D0%B8%D0%BC%D0%B0%D1%86%D0%B8%D0%B8%CC%86%20%D0%B2%20%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%BE%D1%80%D0%B5.jpeg?alt=media&amp;token=a0d4fc32-d1bb-409c-be01-ccbba8ffa42d">Отладка анимаций в редакторе.jpeg</a></td></tr></tbody></table>

<details>

<summary>Пример описания fsm-состояния</summary>

```
{State "stand_bazooka_pick"
    {from "stand"}
    {fromTagged "up"}
    {transit "stand"}
    {to "stand"}
    {tag "up"}
    {tag "bazooka-pick"}
    {condition "knock-down"}
    {split "walk_all" "linked"}
}
```

Блок описывает состояние с именем `stand_bazooka_pick`.

Переход в это состояние разрешён из состояния `stand` или из любого другого состояния, у которого задан тег `up`.

Параметр `condition` указывает, что переход в состояние возможен только при активном запросе `knock-down`.

После завершения текущего состояния FSM автоматически выполнит переход в состояние `stand`, как задано параметром `transit`.

Также из этого состояния разрешён явный переход в `stand`, заданный через параметр `to`.

Состоянию присвоены два тега: `up` и `bazooka-pick`. Эти значения могут использоваться для сопоставления с запросами и в логике обработки состояний.

Параметр `split "walk_all" "linked"` указывает, что в этом состоянии применяется раздельное воспроизведение анимаций: нижняя часть тела может использовать анимацию `walk_all`, в то время как верхняя остаётся синхронизированной с основной последовательностью.

</details>

### Подмножество FSM-состояний

С помощью блока параметра `subset` описываются подмножества состояний, чтобы ограничить список допустимых для выбора состояний в зависимости от текущего FSM-состояния.

> **Структура описания FSM-подмножества**
>
> ```
> {subset "name" from "source_subset"
>   {disable "state_A"}
>   {enable "state_B"}
> }
> ```

<table><thead><tr><th width="145.171875">Параметр</th><th>Описание</th></tr></thead><tbody><tr><td><code>subset</code></td><td>Имя fsm-подмножества.<br>Определяет набор разрешенных и запрещенных состояний для данного поднабора.</td></tr><tr><td><code>from</code></td><td>Имя родительского подмножества, от которого наследуются разрешённые состояния.<br>(Опционально)</td></tr><tr><td><code>disable</code></td><td>Отключает доступ к состояниям, помеченным указанным тегом, в рамках данного подмножества. Может использоваться несколько раз для разных тегов.</td></tr><tr><td><code>enable</code></td><td>Включает доступ к состояниям, помеченным указанным тегом, в рамках данного подмножества. Может использоваться несколько раз для разных тегов.</td></tr></tbody></table>

<details>

<summary>Пример описания fsm-подмножества</summary>

```
{subset "with_item" from "default"
    {disable "down"}
    {disable "squat"}
    {disable "looking"}
    {disable "repair"}
    {disable "step-aside"}
    {enable "open-with_item"}
}
```

Подмножество с именем `with_item` наследует состояния подмножества `default`, но исключает из выборки возможных переходов состояния: лежать (down), сидеть в укрытии (squat), осматриваться (looking), ремонтировать (repair) или стрейфиться (step-aside), так как указанные состояния несовместимы с состоянием, когда юнит несет в руках тяжелый предмет, например, канистру или ящик.

</details>
