Игровой ИИ
September 2

Игровой ИИ. GOAP

В прошлый раз мы начали обсуждать Goal Oriented Action Planning (GOAP), сегодня же мы обсудим несколько нюансов работы с планировщиками. Также не забывайте подписываться на мой канал, чтобы не пропустить следующие части.

Перепланирование.

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

Коллизии планов

Планы могут обладать коллизиями. Например, если оба агента хотят открыть одну и ту же дверь, первой попыткой решить такую коллизию будет создание запрета на доступность объекта (двери), как только агент начал выполнять план. К счастью, все объекты, с которыми взаимодействует наш ИИ, умные объекты, поэтому реализовать такой запрет не составит труда. Но такой подход генерирует другую проблему: если агент не справился со своим планом (например его убил игрок), а план был актуальный, высока вероятность того, что и другой агент посчитает этот план оптимальным. В итоге у нас получится поведение, где один агент за другим пытаются использовать объект, а их убивает игрок. Такое поведение выглядит глупым, поэтому логично добавить не просто флаг, а тайм аут на использование объекта.

Заскриптованное поведение.

Одним из бонусов GOAP является то, что поведение выглядит динамичным и не заскриптованным, но иногда это является проблемой. Очень часто в играх мы хотим каких-то фиксированных реакций на какие-то события. Игрок проводит добивание, соседние рядом противники начинают разбегаться, командир отряда используют способность, остальные участники отряда пытаются его прикрыть и т.д. В архитектуру GOAP не заложены простые способы реализации таких поведений. Можно, конечно, делать скриптованные цели и под них простые планы с реакциями, но такие вещи быстро уничтожают все изящество и удобство GOAP. Одним из способов такой проблемы может быть использование среднеуровневой логики.

Среднеуровневый логика

Со многими вышеуказанными проблемами столкнулась команда Monolith, которая и придумала GOAP для своей игры F.E.A.R. Одним из подходов, к которым пришла команда Monolith, было отвести планеру место в середине архитектурной иерархии. Под этим подразумевается, что не стоит перегружать планировщика информацией о низкоуровневых вещах. Например какую позу или анимацию сейчас играет персонаж, и добавлять в план действие смены позы. Такие действия усложняют планы и делают их создание более медленным с точки зрения производительности. Анимационная система может сама добавить смену позы или анимацию экипировки оружия перед атакой или началом плана. Это позволяет плану концентрироваться именно на действиях и поведении, а не на навигации, анимациях и подготовке. С другой стороны, не стоит перегружать планировщик и верхнеуровневой информацией. Если один ИИ может выполнять несколько ролей, или у него может быть разное состояние (отравлен, сломлен, напуган), не стоит пытаться поместить это все в состояние мира и заставлять планировщик решать такие задачи. Напишите более высокоуровневую систему, которая будет подменять список задач и действий у вашего агента на соответствующий его роли/состоянию. Это упростит и выбор целей, и построение плана.

Hierarchical Task Network (HTN) Planning

Иерархическая сеть задач является смежными с GOAP подходом. В отличие от GOAP, в котором мы ищем план, перемещаясь по графу действий, пока не получим план, удовлетворяющий наши цели, HTN использует декомпозицию задач превращая их в простые атомарные действия.

Пример HTN из плагина Hierarchical Task Network Planning AI

Задача (task)

В HTN задачи могут быть двух типов: примитивные и составные. Примитивные задачи - это одно атомарное действие, схожее с действиями в GOAP. Например, атаковать игрока или подобрать оружие. Целью работы модели является создание последовательности примитивных задач. Примитивная задача состоит из оператора и набора эффектов и условий. Составные задачи состоят из набора методов. Каждый метод состоит из условия и набора задач.

Оператор

Оператор - это простое действие, лишенное контекста. Например, атаковать или переместиться куда-то. Именно задача дает оператору контекст, чтобы стать полноценным действием для ИИ агента. Разные задачи могут использовать одинаковые операторы, но разные контексты, условия и эффекты приводят к разнообразному поведению

Эффекты и Условия

По аналогии с GOAP, эффекты - это то, как изменяется состояние мира при выполнении этого действия, а условия - это то, каким должно быть состояние мира, чтобы эта задача имела смысл.

Построение плана.

HTN начинает с высокоуровневой составной задачи, которая последовательно разбивается на подзадачи, используя методы и условия в них. В результате мы получаем план, который выполняет агент. Например, задача “Выковать меч” может быть разбита на подзадачи “Достать Железо”, “Перейти В Кузню”, “Обработать Железо”. В свою очередь, “Достать Железо” может быть разбито на “Перейти на склад”, “Взять Железо” или  “Перейти в шахты”, “Добыть Железо”, в зависимости от состояния мира, в частности - есть ли железо на складе.

Особенности HTN

HTN обладает такими же минусами и плюсами, как и GOAP, так как по сути этот тоже планировщик, только с подходом сверху вниз к поиску плана. Если вас заинтересовала подробная работа HTN, есть отличная статья на эту тему

Групповые Планы.

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

Простым примером может служить просьба одного агента получить лечение от второго, или открыть дверь с помощью кнопки, недоступной первому. Такие агенты создают еще больше иллюзии интеллекта, так как начинают приходить друг другу на помощь и действовать сообща, даже если речь идет о коротких простых планах.

Готовые решения

Самостоятельная реализация такой модели выбора поведения может быть нетривиальной задачей, требующей понимания архитектуры современных игр. Реализация должна взаимодействовать с множеством других подсистем для создания правильного состояния мира. Часто встает вопрос оптимизации и организации удобной настройки поведения. Поэтому чаще всего лучше использовать готовые решения. Например, для Unreal Engine 5 есть плагин Hierarchical Task Network Planning AI , позволяющий создавать поведения, используя иерархический планировщик.

В следующий раз мы с вами рассмотрим гибридные ИИ, т.е. ИИ, которые сочетают в себе две модели (например, FSM и Behaviour Tree).