October 3, 2024

Игровой ИИ. Behavior Tree.

Продолжаю мой цикл про игровой ИИ. В  предыдущей части мы начали изучать Behaviour Tree. В этой части мы поговорим про Blackboard, сервисы и декораторы. Также не забывайте подписываться на мой канал, чтобы не пропустить следующие части.

Blackboard

Одна из особенностей дерева поведения является то, что в общем случае узлы дерева статичны, и экземпляр дерева и узлов не создаётся только для конкретного агента. Система ИИ конкретного агента хранит текущую активную ветку в форме указателей или индексов, но не все дерево целиком. Это очень важное концептуальное отличие от FSM, так как конечный автомат хранит как раз-таки экземпляр состояния. Конечно же в реальных деревьях существуют имплементации, позволяющие создавать экземпляры узлов для каждого агента, но даже при таком использовании это скорее служит утилитарному удобству (сохранить какие-то вещи или упростить доступ или вычисление условий), чем для реализации поведения. Возникает вопрос - как же агенту хранить информацию и передавать её между узлами дерева? Ответом на такой запрос является Blackboard.

Blackboard в общем случае является системой памяти агента, к которой могут получить доступ не только системы поведения типа дерева поведения, но и любые системы ИИ (в принципе, как и любые другие системы игры, но я настоятельно не рекомендую подход, в котором кто-то кроме ИИ взаимодействует с Blackboard напрямую).

У Blackboard могут быть разные зоны видимости.

  • Локальный - доступен только активному узлу и очищается при переходе к новому узлу. Если такие локальный blackboard не имплементирован, очень часто будут появляться нестатичные узлы экземпляры, чтобы хранить некоторые важные поля для выполнения узла.
  • Blackboard дерева - доступен только конкретному активному дереву и очищается при переходе к другому дереву. Чаще всего используется при сложных вложенных деревьях с абстрактными базовыми деревьями.
  • Blackboard  агента - доступен всему агенту ИИ, и цикл жизни Blackboard совпадает с циклом жизни ИИ.
  • Global blackboard - глобальная память всей игры или, например, некой группы агентов. Может быть использован для переданных данных между агентами.

В UE 5 по умолчанию существует только Blackboard агент. Но имплементация других видов при необходимости не должна вызвать больших затруднений.

Пример Blackboard в UE5. Ключами являются строчки, у каждого ключа строго определен тип данных.

Blackboard в общем случае может быть реализован каким угодно способом. Это может быть просто некая выделенная память, или массив элементов. Но чаще всего для удобства Blackboard реализуют либо как нетипизированный словарь (Map), либо как размеченный словарь. Нетипизированный словарь - это такой словарь, у которого в значении записан не конкретный тип, а указатель на область в памяти, в которой можно хранить любой тип. Размеченный словарь - это такой словарь, к которому прилагается схема, валидирующая типы данных, записанных в конкретном ключе словаря. В роли ключа чаще всего выступают строчки, либо предопределенные Enum.

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

Data flow

Теперь когда мы описали память и базовые узлы дерева поведений, следует написать пару слов о том, как стоит организовывать деревья, и как организовывать передачу данных между их частями. Для начала - любые внешние изменения, на которые должно реагировать агент, должны попадать в Blackboard, а не в узлы (если у них есть экземпляры). Так как дерево поведения является основным и главным местом для выбора поведения агента, то дерево должно считывать информацию из Blackboard и принимать решения о том, как агент должен реагировать. Хорошим примером служит простейший сценарий вступления в бой с противником.

  1. Система детектирования и наблюдения, в случае UE 5 AIPeceptionComponent, замечает противника и отправляет событие.
  2. ИИ Контроллер реагирует на это событие и записывает его в некий список в Blackboard.
  3. Система поведения анализирует этот список и текущее состояние агента. В случае с деревом поведения, у нас есть узел, который проверяет - есть ли доступные цели у агента. Это может быть реализовано непосредственно в узле (мы анализируем список и сразу решаем есть ли подходящие цели), либо асинхронно (тогда дерево может делегировать это отдельному компоненту, который анализирует это самостоятельно). Важно понимать, что даже в случае компонента, дерево принимает решение о том, что мы начинаем анализ, когда мы его начинаем и используем ли мы результат, и как мы анализируем. Т.е. система поведения принимает решения что делать с изменением списка целей. Компонент же выступает в роли библиотеки кода для анализа.
  4. После того, как узел нашел подходящую цель, он записывает его в другое поле в Blackboard как текущая цель.
  5. Все остальные узлы взаимодействуют уже с полем Blackboard и создают боевое поведение для нашего агента.

Другим примером могут служить простейшие действия поиска и перемещение к укрытию. Один узел ищет точку, которая удовлетворяет критериям укрытия, и записывает её в Blackboard. Следующий узел совершает перемещение к точке записанной в Blackboard.

Пример того, как с данными работают в BT. Run EQS Query выполняет запрос к системе EQS и, дождавшись результата, записывает его в Blackboard в поле MoveLocation. Move To совершает навигацию в точку, записанную Blackboard в поле MoveLocation

Декораторы.

В разработке игрового ИИ существуют две крайности. Первая - это массивные сложные действия, которые описывают целое поведение разом. Примером может служить состояние в конечном аппарате, описывающее всё состояние боя разом и не разбитое на подсостояния. В дереве поведения аналогом служит узел, который описывает не действие, а целое поведение. Например узел, который сразу описывает последовательность действий: найти точку, подойти к ней, использовать предмет, начать атаку и т.д. Вторая крайность - это атомарные очень простые действия, описывающие слишком малые части поведения, примером может служить разбиение навигации на поиск пути и передвижение на два отдельных узла в дереве или состояния в конечном аппарате. Первая крайность осложняет переиспользование состояний и узлов, что замедляет разработку ИИ для каждого нового агента и поведения, поэтому, скорее всего, придется писать новую логику. Вторая крайность приводит к тому, что деревья и аппараты разрастаются, что не только ухудшает отзывчивость ИИ, но и усложняет добавление новых поведений.

Одним из инструментов достижения баланса между этими двумя крайностями в дереве поведения служат надстройки над узлами. Одной из таких надстроек является декоратор.

Декоратор - это некоторая логика, выполняемая на этапе обхода дерева и на этапах активации и деактивации узлов. Декораторы располагаются на конкретных узлах дерева. Чаще всего декораторы используются для контроля выполнения дерева. Можно выделить два основных типа декораторов: декораторы условий и декораторы контроля.

Декораторы условий - это логика, которая проверяет нахождение агента в определенном состоянии и актуальность поведения, описываемого выходящим из узла под-деревом, на котором расположен декоратор.

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

Пример декоратора условия. Если у нас существует валидная локация и компоненте детектирования, запишем эту локацию в Blackboard, запустим EQS который найдет позицию для наблюдения за этой локацией. Если такой локации нет, то запустим другой EQS который найдет позицию для наблюдения относительно нашей текущей локации. В любой случае после поиска переместимся в найденную позицию.

В UE5 у декораторов условий есть еще одна дополнительная задача - это прерывание узлов. Декоратор может прервать выполнение узла, на котором он расположен, если условие перестало выполняться. Также декоратор может прерывать узлы, расположенные левее узла с декоратором, если условие выполняется, и узел расположен в селекторе. С помощью таких декораторов можно реализовать поведение аналогичное активным селекторам, когда актуальность узлов проверяется каждый тик дерева, а не только при обходе.

Пример декоратора прерывателя. Как только на нашем агенте появляется заданный тег  (AI.Detection.Combat) мы прерываем выполнение узлов MoveTo или Wait и запускаем RunAbility. Если этот тег убирается с агента, мы прерываем узел RunAbility и инициируем дальнейший обход дерева.

Декораторы контроля - это правила, изменяющие то, как дерево выполняется и/или обходится.

Примерами таких декораторов могу служить:

  • Декоратор успеха - декоратор подменяет статус выполнения узла на Успех (Complete), что влияет на логику дальнейшего обхода
  • Декоратор цикла - декоратор перехватывает статус завершения узла и запускает его заново.
Пример использования декоратора успеха: агент ожидает выполнения текущей способности, после чего пытается получить локации из компонентов детектирования. Если это не получается, или навигация до этой локации невозможна, то узел в любом случае завершается успехом, и агент ждет несколько секунд. В случае успешной навигации агент будет ждать уже после навигации.

Сервисы

Сервисами являются надстройки над узлом, которые выполняют некоторую логику в разные моменты цикла выполнения узла. Это может быть логика, которая выполняется при обходе дерева, активации узла, деактивации узла, или периодически по времени, пока узел активен. Основная особенность сервисов в том, что они напрямую не влияют на обход или выполнение дерева. Они скорее служат вспомогательными модулями, которые добавляют контекст или дополнительные данные для поведения узла, к которому они принадлежат. Примером сервисов могут служить следующие модули:

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

Сервисы позволяют изменять поведения и создавать вариативность не создавая и не меняя узлы, которые эти поведения описывают.

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

В следующий раз мы обсудим вложенные деревья и попробуем собрать простой игровой ИИ на дереве поведений UE5.