Найти тему

Создание ARPG в одиночку. Часть 2/ Искусственный Интеллект

Оглавление

Лирика

Одна из основных деталей во всех играх с экшеном - искусственный интеллект (в дальнейшем просто ИИ). Его поведение может как задавать атмосферу, так и поведение игрока. Поэтому буквально сразу после того как "научил" персонажа ходить и пользоваться инвентарём, я принялся за написание ИИ.

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

Игра над которой я работаю
Игра над которой я работаю

Тупой, но не слишком....

Напоминаю, что я делаю прежде всего изометрическую рпг с кучей экшена, из этого появляются первые требования к ИИ:

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

Построение начального алгоритма

Я рассказывал про него в одном из своих видео, но постараюсь так же кратко рассказать всё при помощи печатного текста

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

  • Статы - фиксируют количество здоровья и эффекты на мобе
  • Регистратор - проверяет видит/слышит ли моб игрока
  • Поведение - оно говорит что делать если игрок был замечен, по какой тактике его бить(или убегать)
  • Движение - принимает позицию в которую надо двигаться, и, собственно, управляет скоростью
  • Атака - решает когда надо атаковать, так же задаёт свойства атаки
  • Диалог (о нём поподробнее чуть позже) - решает что и как сказать игроку
  • Анимация - принимает решение какую анимацию воспроизводить исходя из остальных модулей
  • Звук - чтобы моб мог топать при ходьбе и кричать при атаке
  • Смерть - не все мобы просто валяться замертво, у некоторых есть эффекты при смерти (банальный взрыв или разделение на несколько частей), поэтому вывожу её в отдельный класс
-2

Про код чуть-чуть подробнее

Сразу же выделю интерфейс взаимодействия. Важнейшее связующее между блоками

public interface IAi
{
void Detected(bool value); //Даёт понять виден игрок или нет
void Agression(bool value); // Атаковать игрока?
void ActionDelay(bool value); //Если играет анимация атаки или урона, модули приостановят работу
}

Далее обращусь к регистратору, оговорка на то что он начинает отслеживать игрока только при его приближении (входе в триггер моба)

private void OnTriggerEnter(Collider other)
{
if(other.tag == "Player")
{
Stats.playerStats.AddEnemy(stats);
playerOnTrigger = true;
}
}
private void OnTriggerExit(Collider other) {
if(other.tag == "Player") { Stats.playerStats.RemoveEnemy(stats);
playerOnTrigger = false;
Ai.DetectedSwitch(gameObject, false); }
}

И если игрок в триггере ИИ, то идёт проверка на "слышит" или "видит" моб персонажа.

if(!detected && !actionDelay)
{
if (curAgressionTimer > 0)
curAgressionTimer -= Time.deltaTime;
else
if(aggression)
Ai.AgressionSwitch(gameObject, false);
if (playerOnTrigger)
{
if (CheckHear() || CheckWatch())
{
Ai.DetectedSwitch(gameObject, true);
}
}
}

В основном в модулях нечего особо интересного нет, поэтому пожалуй остановимся на поведении

В нём используется несколько вариантов агрессии от пассивной, до мордобоя, и несколько видов тактик:

  • Simple - просто бежит на игрока с шашкой наголо
  • Back - примерно тоже самое что и Simple, но пытается зайти сбоку или сзади
  • Bite - когда персонаж отвернулся то ИИ нападает, а когда разворачивается убегает
  • Run - просто убегает от игрока

Методы получились очень маленькие, даже я не ожидал такого...

static void SimpleAttack(AiMove aiMove) => aiMove.SetTarget(Stats.playerStats.Position());
static void BackAttack(AiMove aiMove) => aiMove.SetTarget(Stats.playerStats.LocalBackward(2)); s
tatic void BiteAttack(AiMove aiMove) {
if (Vector3.Angle(Stats.playerStats.Position() - aiMove.Position, Stats.playerStats.Forward()) <= 60) SimpleAttack(aiMove);
else
Run(aiMove); }
static void Run(AiMove aiMove) => aiMove.SetTarget(Stats.playerStats.LocalForward(5));

На этом всё самое интересное кончается :с
Если хочется побольше узнать то я успел записать видео-разбор:

Итог

На данный момент всё смотреться сыровато. Но тем не менее фундамент заложен, и я думаю что он не плох.

Проведя пару тестов я понял, что добился того чего хотел, ИИ ведёт себя просто и понятно, а в больших количествах начинает играть новыми красками. То что надо для моей игры :)

-3

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

Обещанные исходники

Первая часть блога