С ростом проекта в него приходит постоянно приходит новая аудитория: как матерые игроки, так и новички в жанре. Закинуть всех в один матч можно, но первым будет скучно, а вторым — сложно. В итоге всех можно потерять уже на старте.

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

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

Изначально в Pixel Gun 3D вообще не было мультиплеера. Уже в процессе быстрого роста и неожиданной популярности мы поняли, что без него нам не обойтись. Когда он был готов, то встал вопрос сделать комфортный онбординг новичков — чтобы они не страдали в самом начале от ветеранов шутера и не уходили навсегда. 

Задачей стало добавить ботов, которые бы сами адаптировались под навыки приходящих геймеров. То есть если приходят сильные игроки, то боты автоматически становятся сильнее, если слабые — слабее. И это бы не зависело от уровня пользователя и наигранных часов, например, даже на максимальном уровне игрок может быть слабым по скиллу.

Сделать это нужно было максимально быстро и с минимально возможными ресурсами (мы тогда с головой были в разработке нового контента). Второй момент — боты должны быть реалистичными, чтобы новичкам было не скучно и они не могли определить, что играют не против людей.

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

Поведение ботов

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

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

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

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

У нас сетевая игра, где персонажем управляет человек. Есть формат данных, который нам присылает игрок — где находится, куда смотрит, какая у него анимация, состояние, шмот, пушка. Логика бота про префабу игрока отсылает то же самое, в том же формате, чтобы сократить время реализации, подключения к уже существующей логике.

Глобально у ботов есть три состояния:

  1. Покоя — когда противников рядом нет и бот просто передвигается по карте.

  2. Атаки — когда обнаружен противник и начинается перестрелка.

  3. Поиска — когда противник, с которым бот перестреливался, исчез из прямой видимости.

Состояние покоя

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

Первая реализация

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

Вид глазами геймдизайнера. Так происходила запись маршрутов между точками.
Вид глазами геймдизайнера. Так происходила запись маршрутов между точками.

Вид глазами геймдизайнера. Так происходила запись маршрутов между точками. 

Эти маршруты в дальнейшем использовали боты — выходили на один из сохраненных путей и шли по нему пока не встречали противника. При этом они не только двигались из точки А в точку Б, но и смотрели по сторонам, останавливались, оглядывались. В общем, имитировали человеческое поведение.

Как это выглядело в редакторе:

Первая реализация маршрутов для ботов
Первая реализация маршрутов для ботов

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

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

Вторая итерация

Сейчас на проде — система с контрольными точками по карте. Теперь играть самому стало не нужно: открывается сцена, расставляются точки, проверяются связи между ними. Сами связи для создания маршрутов добавляются либо автоматически, либо настраиваются вручную. Можно сохранить систему для конкретного режима игры, например, для захвата флагов у ботов одни кластеры с точками интереса, а для командного боя — другие.

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

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

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

Окно редактора с расставленными контрольными точками. Голубым цветом выделены поверхности NavMesh, по которым бот может прокладывать путь. Синие стрелки — OffMeshLink. Когда их достигает бот, то попадает в блок кода, который управляет его перемещением по ним.
Окно редактора с расставленными контрольными точками. Голубым цветом выделены поверхности NavMesh, по которым бот может прокладывать путь. Синие стрелки — OffMeshLink. Когда их достигает бот, то попадает в блок кода, который управляет его перемещением по ним.

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

Если бот решил, что ему нужна аптечка или броня, то с помощью NavMesh вычисляется путь до бонуса через NavMesh.CalculatePath. Местоположение всех активных бонусов на карте он получает из отдельного класса, который управляет бонусами — BonusController. Выберется тот бонус, путь к которому имеет состояние NavMeshPathStatus.PathComplete или NavMeshPathStatus.PathPartial. В последнем случае начнется проверка: далеко ли самая последняя точка вычисленного пути (ууспело рассчитать) лежит от бонуса. Если приемлемое расстояние, то бот побежит даже по не до конца построенному пути.

Состояние атаки

Бот знает только об игроках, которые находятся непосредственно в его зоне видимости (препятствия тоже мешают его обзору). Здесь никаких читов — все по-честному. Боты обнаруживают игроков в стандартном для всех пользователей Field of View. Если ему на глаза попался противник (если их несколько, то выбирает случайного), то он решает атаковать или нет.

Чтобы бои были действительно интересными, поведение ботов настраивается под поведение настоящих игроков: бот может убежать, перейти в ближний бой и даже использовать гаджеты, при движении стрейфится. Для этого в состоянии атаки выполняется цикл с различными проверками: сколько HP у бота, сколько у него целей, где расположена цель и так далее. Бот принимает определенное решение по результатам проверки и случайного выбора на основе своего AI-level.

Динамический AI-level

Чтобы разноуровневым игрокам было одинаково интересно играть, боты автоматически меняют свой AI-level (скилл), основываясь на киллрейте (количество убийств к смертям) бота, игроков и текущем счете. Таких уровней у нас сейчас настроено 4, но их может быть любое количество.

Когда киллрейт выходит за заданные границы диапазона для своего уровня, то меняется в какую-либо сторону. Если нам нужно, чтобы бот стал сильным и напал на кого-то, ставим высокий AI-level: он почти не промахивается, чаще бежит, от него сложнее скрыться, а поведение становится агрессивным. Если нужно, чтобы он был слабым, уменьшаем AI-level: он реже стреляет, чаще промахивается, медленнее бегает, меньше стрейфится.

От AI-level зависит множество параметров (чем он выше, тем бот сильнее в своих параметрах):

  • расстояние для выбора цели;

  • выбор другого бота или игрока, если есть и те и другие на доступном расстоянии;

  • должен ли попытаться пойти за бонусом (патроны, броня, хп);

  • должен ли уклоняться при сражении в ближнем/дальнем бою;

  • должен ли прыгать при сражении в ближнем/дальнем бою;

  • должен ли догонять цель при сражении;

  • как долго будет догонять игрока;

  • должен ли стрелять бот с уроном, или только визуально показывает стрельбу;

  • минимальные и максимальные значения для прицеливания у бота;

  • делает паузы при стрельбе или нет;

  • скорость поворота в бою (когда у бота есть цель в прямой видимости);

  • скорость наведения на врага;

  • переключение на стреляющего в него игрока;

  • бросает ли гранаты или меняет оружие, когда нужно перезарядиться;

  • какой тип оружия он может взять;

  • для баланса команд, если вдруг команда начинает проигрывать, и у нее есть боты, то им поднимается уровень.

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

Параметры задают шанс для выбора действия или значения параметра. Он определяется довольно просто. Есть число 0.88 в качестве параметра, выбираем от 0 до 1 с плавающей точкой: если попали в диапазон от 0 до 0,88 — успех, действие будет выбрано, от 0,89 до 1 — неудача. Значение параметра выбирается случайно от заданного минимального значения до максимального, чтобы, например, та же точность не была всегда одинаковой. Некоторые параметры считаются каждый раз, когда нужно совершить какое-либо действие (точность выстрела), другие — несколько раз в секунду (для выбора решения о действии, например, бросок гранаты). 

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

Состояние поиска

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

Вместо заключения

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

Боты вообще очень гибкая штука. Мы их включаем, когда нужно заонбордить новичка или когда не хватает игроков, чтобы заполнить комнату. При этом, если реальные пользователи подключаются, то они бесшовно вытесняют ботов. Это все происходит без участия пользователей и какого-либо голосования «добавляем ботов или нет» — матчмейкинг видит, что комната занята ботами, поэтому он коннектит игрока, а бот просто уничтожается со всем своим прогрессом на карте. Для внимательного игрока это будет выглядеть будто один игрок ушел и на его место быстро нашлась замена.

Комментарии (2)


  1. OlegPatron92
    28.10.2021 18:25
    +1

    Ребята, извиняюсь за оффтоп, подскажите хорошую статью (желательно на хабр) по написанию AI для 3d racing игр.


  1. Kriptoserg
    20.11.2021 16:39
    +1

    Спасибо за статью, про AI всегда интересно.