Герои научились продавать излишний лут + разбор того как это реализовано

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

После того как у героя появляется больше одного оружия, планировщик добавляет задачу "торговать" и агент продает все излишки кузнецу

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

Как это работает?

Представьте что вы одновременно таксист Убера и его конкурента (например, Lyft). Все что вы умеете это куда-то ездить, ждать и нажимать на кнопки. У вас есть одна очередь заказов в которую постоянно кидают новые поездки и ваша задача это решить что делать в данный момент.

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

Для прокладки маршрута у нас используется классический A* с эвристикой. В качестве структуры для поиска пути используется плотная регулярная сетка (dense navigation grid) у которой можно менять размер ячеек (в основном это для отладки).

Для отладки, сетку навигации можно настроить динамически.

Помимо собственно поиска пути который по сути прокладывает рельсы от точки A к цели, агенты также должны уметь обходить динамические препятствия. Для этого есть несколько стратегий, часть из которых у меня уже реализованы. Самые популярные общие варианты решений – это перестройка пути (полного или частичного) и "рулевое управление" (steering). Суть рулевого управления в том чтобы к основному вектору движения (forward vector) добавить корректирующий вектор (repulsion) который будет отталкивать агента от препятствий которые возникли на пути. Отталкивающий вектор обычно обратно пропорционален расстоянию или квадрату расстояний до всех препятствий на пути к цели (можно использовать скалярное произведение с forward vector).

Оранжевым – вектор отталкивающий агентов от препятствий.
Пример того как можно посчитать отталкивающий (repulsion) вектор.
Пример того как можно посчитать отталкивающий (repulsion) вектор.

Кроме поиска пути система ИИ также нуждается в быстром поиске объектов на карте. Для этого у меня используется другая сетка регионов (spatial grid) которая запоминает какие объекты находятся в каких регионах. Для поиска в такой сетке нам нужно просто вычислить координаты региона и перебрать все сущности внутри него и во всех регионах в некотором радиусе. Похожий метод используется например в RimWorld. Внутри регионов можно например хранить номера сущностей (id), их AABB (для пересечения) и маску слоев.

На примере внизу – визуализация того что хранится в такой сетке для каждого слоя (1 = препятствия, 2 = существа, 4 = предметы, 8 = структуры), каждая сущность при этом может раcсполагаться на нескольких слоях одновременно (для этого в маске слоев нужно присвоить бит этого слоя единице).

Слои в spatial index grid.

Поиск радиусе в сущностей с помощью сетки регионов может выглядеть так:

Обход динамических и статических сущностей в нужных регионах, затем проверка расстояния (либо пересечения по AABB) и пересечения по слоям. Результат пишем в пре-аллоцированный буфер.
Обход динамических и статических сущностей в нужных регионах, затем проверка расстояния (либо пересечения по AABB) и пересечения по слоям. Результат пишем в пре-аллоцированный буфер.

Из других деталей ИИ, у нас есть множество ролей для наших агентов, каждая из которых обладает собственным набором возможных вариантов поведения. Каждый из этих вариантов поведения генерирует серию задач для агентов, таких как блуждание, исследование карты, отдых, торговля, сбор лута, создание предметов, охота на монстров и так далее. Эти задания могут генерироваться периодически или ситуативно, а также "выталкивать" друг друга из очереди задач героя. Например задача отдыха выталкивает все незаконченные задачи исследования карты и охоты чтобы получить новые заказы следующим утром.

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

В настоящее время правила планировщика жестко закодированы, но я подумываю о том, чтобы попробовать другие методы для повышения его эффективности в будущем. Одна из возможностей – это интеграция нейронных сетей используя WGSL рантайм для ONNX (определенно не в альфа-версии). Нейронная сеть, в данном контексте, могла бы определить, должен ли агент выбрать бегство или бой, принимая во внимание такие переменные, как характеристики агента, мощь противника или другие уникальные черты.

Кстати если кому интересно, у проекта появился англоязычный дискорд:

3535
15 комментариев

Я выстрадываю своё решение в похожем направлении, лови пару наработок:
1. А* можно применять не только к карте, но и к взаимодействиям. В том числе и к планировщику.
2. Добавь неписям эмоции. У человека это (список не полный):
- "да!"/эйфория/эндорфин
- "ок"/ожидание порядка/дофамин
- "нет"/"ожидание боли/кортизол
- "ща будут бить"/норадреналин
- "ща буду бить"/адреналин
- "Я КРУТОЙ!!!!"/тестостерон
- "Детали!!"/эстроген (перерабатывается из тестостерона маткой)
- "голод"/(забыл)
- "сытость"/(забыл)
- "поднять вольтаж!!"/ГАМК
- "снизить вольтаж!"
- "поднять реактивность!"/(забыл)
- "узбагойся"/глицин
Суть эмоций - формировать временный "контекст".
"Плохое настроение - это когда ты с удовольствием на всё раздражаешься"Это позволяет относительно дёшево формировать "фоновое поведение", чтобы персонаж, впавший в боевой транс, нуждлся во времени "прийти в себя". То же с истериками, то же с жадностью, с чем угодно. Но может быть и шок из-за испуга.
При этом - убирается проблема "залочившийся стейтмашины". Да, нужно время "оттаять", зато - без "мёртвых петель".
Так что можешь "эмоциями" хоть Бартла 2.0 сделать.

Был бы рад показать/предоставить своё поделие, но я реально его выстрадываю и сейчас кисну над реализацией памяти.

PS: чисто пожелание.
А можно потом доработать диалоги и в этом же мире запилить РПГ? Могу помочь с конлангом (toki pona*, tuki tiki - готовые, мои разработки** aa iu/siu siu - в целом готовые, но словарь не доделан), чтобы с переводами один раз пострадать.

* - 60к знающих активных пользователей как минимум.
** - обрезанный на максимум микс русского, цыганского и японского языков. Внезапно, сработало довольно неплохо.

4
Ответить
Автор

Много советов, спасибо за дельный фидбек)

1
Ответить

Комментарий недоступен

3
Ответить
Автор

Я тоже)

2
Ответить

Используй больше переменных для тонкой настройки. И потом иорайся с коэффициентами.

Например : идти продавать лишнее , не когда этоого лишнего становится X - экземляров.
Сейчас у тебя:
После того как у героя появляется больше одного оружия, планировщик добавляет задачу "торговать" и агент продает все излишки кузнецу

Есть смыл поиграться. Добавить больше переменных ,а потои крутить и выбирать что лучше выглядит с точки зрения зрителя.

2
Ответить
Автор

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

1
Ответить

Нихуя не понял, но очень интересно (с)
Не шарю в программировании дальше третьей главы по Шарпу на метаните, но звучит очень интересно.
Отталкивающий вектор обычно обратно пропорционален расстоянию или квадрату расстояний до всех препятствий на пути к целиИнтересно, как вообще человек, который это придумал, пришел к этому
А некоторые проггеры ещё втирают, что матеша не нужна

1
Ответить