Деревья поведения (Behavior Trees)
- О чём эта тема
- Способ описывать сложное поведение ИИ иерархией узлов: композиты, декораторы и действия, статусы Success/Failure/Running — и инструмент Unity Behavior, реализующий эту идею графически.
- Аннотация
- Конспект начинается с ограничений машины состояний из A11: с ростом числа состояний количество переходов растёт быстрее, чем их можно поддерживать. Вводится дерево поведения: три категории узлов, три статуса, обход от корня на каждом такте игры и итеративное расширение логики без правки готовых ветвей. Обход дерева закрепляется интерактивным тренажёром с классической парой ветвей «бой/патруль». Затем рассматривается пакет Unity Behavior: чем граф поведения отличается от классического дерева, какие типы узлов добавляет и как данные передаются через blackboard; приводится пример собственного узла-действия на C#.
- Пререквизиты
- A11 — машина состояний и NavMesh; A07 — жизненный цикл, наследование; A10 — события и разделение ответственности.
- Мотивация
- Охранник из A11 умеет две вещи: стоять и преследовать. Добавим патрулирование, поиск аптечки, укрытие и крик о подмоге — и машина состояний начнёт задыхаться: каждое новое состояние требует переходов ко многим существующим, схема превращается в клубок. Деревья поведения решают проблему масштабирования: новая способность — это новая ветвь, а не десяток новых стрелок. Именно поэтому они стали отраслевым стандартом ИИ персонажей.
1. Устройство дерева поведения
Дерево поведения — иерархия узлов, которая при обходе сверху вниз задаёт логику поведения агента. Работа с ним итеративна: простой узел позже разворачивается в собственное поддерево с более детальной логикой. Расширение дерева вниз добавляет подробности, расширение вширь — альтернативные варианты поведения; и то и другое не требует переделывать уже готовые ветви.
Каждый узел, отработав, возвращает родителю один из трёх статусов: Success (успех), Failure (неудача) или Running (выполняется). Running — ключевой статус: он означает «я ещё не закончил, продолжим на следующем такте». Узел патрулирования возвращает Running всю дорогу до точки и лишь затем Success или Failure. Благодаря этому дерево не обязано выполняться целиком за один кадр — его работа растягивается на много игровых циклов, и сложность дерева почти не влияет на производительность. Классическое дерево при этом обходится от корня на каждом такте.
Узлы делятся на три категории:
Два главных композита. Sequence («последовательность», логическое И): выполняет детей по порядку; если ребёнок вернул Success — переходит к следующему, Failure — вся последовательность Failure, Running — Running. Selector («выбор», логическое ИЛИ): пробует детей по порядку; Failure — пробует следующего, Success или Running — возвращает его. Из этих двух кирпичей собирается типовая логика «если можешь — дерись, иначе патрулируй»:
Дерево охранника: Selector выбирает между ветвями «Бой» и «Патруль». Меняйте обстановку флажками и нажимайте «тик» — узлы подсвечиваются статусами (зелёный — Success, красный — Failure, оранжевый — Running). Заметьте: пока игрок не виден, ветвь боя проваливается на первом же условии, и Selector уходит в патруль.
Нажмите «тик», чтобы выполнить один обход дерева.
2. Unity Behavior: граф поведения
Долгое время в Unity не было штатного инструмента для такой логики — разработчики покупали решения в Asset Store или писали свои. В конце 2024 года Unity выпустила пакет Unity Behavior — графовый редактор логики ИИ: граф прикрепляется к объекту сцены и меняет его поведение в зависимости от состояния игры.
Граф поведения умеет всё то же, что классическое дерево, но расширяет модель:
- привычные категории узлов переименованы: листья — action-узлы, декораторы — modifiers, композиты — sequencing-узлы;
- добавлены join-узлы, сливающие ветви (дерево превращается в граф), conditional-узлы для ветвления по условиям (аналог if/else и switch) и event-узлы, отправляющие и слушающие события;
- каналы событий связывают разные графы между собой — граф работает изолированно, но общается с другими графами и данными игры;
- подграфы можно прерывать, повторять и подменять по внешним событиям;
- статусов пять: к Success, Failure и Running добавлены Waiting и Initializing;
- в отличие от классического дерева, обход не обязан идти от корня каждый такт — есть узлы, повторяющие только часть графа до выполнения условия.
Действия в action-узлах могут «складываться» в столбик: визуально они выглядят одним блоком, но выполняются как независимые действия — нижнее не начнётся, пока статус верхнего не позволит:
Так граф выглядит в работе. Слева — простой граф: условное ветвление Branch on по переменной blackboard (HasEatenPill) и повторение ветви через Repeat While — «призрак» убегает от героя, съевшего пилюлю, и преследует его в противном случае. Справа — полная версия того же поведения с зонами обнаружения и сменой цели:
3. Blackboard и собственный узел-действие
Дереву нужно откуда-то брать данные об обстановке: видит ли агент игрока, сколько у него здоровья. Для этого служит blackboard («классная доска») — хранилище пар «ключ — значение», общее для всего дерева. Скрипты записывают туда факты, узлы дерева читают их в условиях. Так дерево отделяется от кода игры: сенсоры пишут на доску, поведение читает с доски.
Логика конкретного действия оформляется собственным узлом — классом-наследником базового узла-действия. Пример из руководства к одной из реализаций деревьев (комментарии переведены): узел, меняющий спрайт владельца дерева.
public sealed class DisplaySprite : BT_ActionNode { public Sprite sprite; // спрайт, который нужно показать private SpriteRenderer spriteRenderer; // вызывается один раз до первого обновления: читаем владельца с blackboard protected override void OnInit() { GameObject obj = blackboard.GetBlackboardValueByKey<GameObject>("Owner"); spriteRenderer = obj.GetComponent<SpriteRenderer>(); } // вызывается, когда дерево выполняет это действие protected override ENodeState Execute() { if (spriteRenderer != null) { spriteRenderer.sprite = sprite; state = ENodeState.Success; } else { state = ENodeState.Failed; } return state; } }
Каркас везде одинаковый: инициализация читает нужные ссылки (часто — с blackboard), метод выполнения делает работу и возвращает статус. Точные имена базовых классов и методов зависят от инструмента — у Unity Behavior свой набор базовых типов, но устройство узла то же. А скрипт-«сенсор» с той стороны доски выглядит знакомо: обычный MonoBehaviour, который в Update записывает значение ключа (blackboard.SetBlackboardValue("ShowImage", switchSprite)).
Когда что выбирать. Машина состояний из A11 проще и отлично подходит для двух-четырёх состояний. Дерево поведения окупается, когда вариантов поведения много и они будут расти: для курсового проекта разумно начать с FSM и перейти на дерево, если файл с switch перестал помещаться в голове.
Контрольные вопросы
-
В FSM новое состояние требует переходов ко многим существующим — число стрелок растёт быстро. В дереве новая способность — это новая ветвь: расширение вниз уточняет логику, вширь — добавляет альтернативы, и готовые ветви менять не нужно.
-
Success, Failure и Running. Running означает «действие не закончено, продолжим на следующем такте»: благодаря ему дерево не выполняется целиком за кадр, длительные действия (дойти до точки) растягиваются на много игровых циклов, и сложность дерева не бьёт по производительности.
-
Композитные — единственные с несколькими детьми, управляют порядком выполнения (Sequence, Selector); декораторы — ровно один ребёнок, преобразуют его результат; листья — узлы-действия без детей, в них сама логика поведения.
-
Sequence — логическое И: дети по порядку, Success ведёт к следующему, первый Failure проваливает всю последовательность. Selector — логическое ИЛИ: пробует детей по порядку, Failure ведёт к следующему, первый Success или Running возвращается наверх.
-
Общее для дерева хранилище «ключ — значение»: скрипты-сенсоры пишут факты, узлы читают. Прямой обмен данными между узлами связывает их друг с другом — перестановка ветвей ломает логику; через доску узлы остаются независимыми.
-
Join-узлы (сливают ветви — дерево становится графом), conditional-узлы (ветвление по условиям, аналог if/else и switch) и event-узлы (отправка и прослушивание событий, включая каналы между разными графами).
Источники
- Akritidis, G. How To Use Unity's Behavior Graph For AI With Behavior Trees. Part 1 // C# and Unity development : [блог]. — 2024. — URL: https://giannisakritidis.com/blog/Behavior-Graph-And-Behavior-Trees-Part1/ (дата обращения: 08.07.2026).
- Unity Behavior : User Manual // Unity Documentation : [сайт]. — URL: https://docs.unity3d.com/Packages/com.unity.behavior@1.0/manual/index.html (дата обращения: 08.07.2026).
- Behavior Tree quick tutorial : [руководство к реализации деревьев поведения]. — [Электронный ресурс, сохранённая копия в материалах курса].