Как создать игру, ничего не умея. Часть третья: искусственный интеллект
Предисловие
Привет, народец, вот и подошло время для очередного поста, в котором посредством уроков и статей в интернете я пытаюсь создать игру. С каждым новым постом этот процесс отнимает все больше времени, ведь проверить на работоспособность все возможные варианты очень муторно, но я не унываю и становлюсь всё ближе к завершению своего эксперимента.
Предыдущие статьи вы можете найти по ссылкам
- Как создать игру, ничего не умея. Часть первая: модели и анимации
- Как создать игру, ничего не умея. Часть вторая: сцена (уровень), квесты, интерактивные объекты
- Unity: работа с Terrain
Сегодня мы с вами займёмся боевой системой и врагами.
Сцена
Для начала мы, уже зная как создаются сцены, создадим уровень, в котором будем воевать с нашими врагами. Создаём сцену — я назвал её Level_02, наполняем декорациями, и добавляем модели врагов.
В качестве модели врага я снова буду использовать рыцарей. Поставлю одного, дам ему имя Enemy_01, а после полной настройки добавлю его в папку Prefabs, и уже оттуда буду использовать полностью готовых рыцарей-врагов. Делаю это потому, что нет желания ставить несколько врагов, а после каждого по-отдельности настраивать. Лень — наш главный двигатель!

Искусственный интеллект
Приступим к настройке вражины! Создаю новый Animator Controller и даю ему название Enemy! Туда мы добавляем три состояния: покоя, ходьбы и удара. Так как анимации удара у нас не было, беру её с mixamo и добавляю в проект. Прописываем все транзисторы и даём названия Bool -> eWalk и eHit. Приставку Е я использую для понимания, что они принадлежат Enemy (можно этого и не делать, но мне так удобнее). Получаем такую картину:

А сейчас нам нужно научить наш ИИ видеть всё что его окружает, и поможет нам в этом NavMeshAgent.
Переносим в сцену префаб Player и добавляем ему компонент NavMeshAgent. Выбрав нашего Player, переходим во вкладку Navigation -> Objects, с зажатой клавишей ctrl нажимаю на все объекты деревьев и гор, перехожу во вкладку Bake и нажимаю на кнопку Bake. Теперь у нас есть поверхность, по которой может передвигаться как наш персонаж, так и ИИ.

Анимация и окружение готово, пришло время сказать нашему ИИ, что он очень не любит нашего персонажа и хочет его атаковать всегда, когда видит.
Создаём пустой объект GameObject — > Create Empty, выставляем ему значения Transform такие же, как и у нашего Enemy_01, после переименовываем пустой объект в Enemy и добавляем в него объект Enemy_01. Должно получиться вот так:

Больше Enemy_01 мы трогать не будем и займемся настройкой объекта Enemy. Добавляем ему компонент NavMeshAgent — это позволит нашему ИИ передвигаться по нашей сцене. После очередной кучи материала из интернета, я всё же запустил правильную работу ИИ, и заставил его смотреть на меня и атаковать, когда я нахожусь на достаточном расстоянии от него.
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI;
public class Enemy: MonoBehaviour
{
public float DistanceToWalk;
public float DistanceToHit;
public float curDistance;
public GameObject Player;
public Animator anim;
public bool eWalk;
public bool eHit;
public float Hit;
public float DelayBetweenAnim;
public float DelayeHit;
UnityEngine.AI.NavMeshAgent _agent;
// Start is called before the first frame update
void Start()
{
_agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
}
// Update is called once per frame
void Update()
{
float distance = Vector3.Distance(Player.transform.position,transform.position);
curDistance = distance;
if (distance > DistanceToWalk)
{
return;
}
if (distance < DistanceToWalk && distance > DistanceToHit)
{
//Преследовать
_agent.SetDestination(Player.transform.position);
anim.SetBool(«eWalk», true);
}
if (distance < DistanceToWalk && distance < DistanceToHit)
{
//Преследовать и кусать
_agent.SetDestination(Player.transform.position);
transform.LookAt(Player.transform);
if (!eHit)
{
StartCoroutine(Mlee());
}
}
}
IEnumerator Mlee()
{
eHit = true;
anim.SetBool(«eHit», true);
yield return new WaitForSeconds(DelayeHit);
anim.SetBool(«eHit», false);
yield return new WaitForSeconds(DelayBetweenAnim);
eHit = false;
}
}
Не забываем, что сайт зачем-то меняет все (") на (<), и правим огрехи уже в программе. В скрипте есть строки, которые можно настраивать в зависимости от ваших расстояний и нужд, у меня же настройки получились вот такими:

Сразу скажу, что в строке Player мы добавляем объект нашего игрока (у вас он может иметь другое имя), а в строке Anim — добавляем анимацию из объекта Enemy_01 (и снова у вас она может иметь иное название), в зависимости от имен ваших объектов. Пора проверить как оно работает!
![]() | ![]() | ![]() |
За счет строки curDistance мы видим на каком расстоянии мы находимся от нашего Enemy, и пока мы находимся дальше 20, как и было прописано в настройках, ИИ не реагирует, но стоит приблизиться, как он сразу же начинает двигаться в нашу сторону, а когда подходит достаточно близко, начинает атаковать. Я считаю, что всё работает.
Значит пора и нашему персонажу научиться бить по врагам! Добавляем в Animator Controller анимацию удара, протягиваем транзисторы, Bool будет называться punch. Дописываем пару строк в скрипт Move и тестируем!
{
if (Input.GetButtonDown(«Jump»))
GetComponent<Animator>().SetBool(«punch», true);
else if (Input.GetButtonUp(«Jump»))
GetComponent<Animator>().SetBool(«punch», false);
}
Теперь когда я нажимаю на пробел, у меня проигрывается анимация удара, а как только отпускаю — прекращается. Немного криво, но мы и не пытаемся сделать реалистичную игру =) Что же, давайте поместим оружие в руку, а то биться воображением не всегда выходит удачно =)
Интерактивные предметы
Покопавшись в папке с префабами, я обнаружил нужный мне меч, его мы уже использовали для квеста, а сейчас он нам послужит по своему прямому назначению. Добавляю его в сцену, переименовываю в Sword и создаю пустой объект GameObject -> Create Empty, назову его Weapon. За счет того, что у меня персонаж с пальцами, я могу сразу добавить к пальцам меч, но буду использовать пустой объект, прикрепляю его к пальцам, после переношу в него Sword, и обнуляю позицию. Меч точно встаёт в руку.

Проделываем ту же процедуру с Enemy. Я сразу вкладываю меч в руку, немного корректирую положение, и смотрю на результат.

Всё отлично работает, теперь мы машем мечами как в любом фильме про рыцарей =). На этом я закончу сегодняшнюю статью, а продолжение будет еще интересней: наконец перейдём к убийству мобов, получению уровней, и интерфейсу!
Заключение
Очередная статья и очередное движение по нашему плану, мы всё ближе к финалу, осталось совсем чуть-чуть. Если приложить немного усилий и много терпения, уже на этом этапе можно создать полноценную игру! А с вами мы встретимся уже в следующей статье, всем бобра!
П. С.
Сейчас много работы, поэтому не удаётся уделить достаточно времени написанию статей, а еще я начал стримить по вечерам =) но приложу все усилия, чтобы следующая статья вышла ранее чем через неделю.
17 комментариев
Добавить комментарий
Добавить комментарий