Вот мы и на 12-м дне из 100 дней VR. Вчера мы рассмотрели возможности моделей ригов и системы механики Unity (которую я должен был изучить, но проигнорировал в туториале по Survival Shooter…)

Сегодня мы продолжим работу после создания нашего контроллера анимации.

Мы собираемся создать компонент навигации для нашего Knight Enemy, чтобы преследовать и атаковать игрока. Как вы, возможно, помните, Unity предоставляет нам ИИ-навигатор, который позволяет нашим игровым объектам двигаться в определенном направлении, избегая при этом препятствий.

Перемещение врага к игроку

Настройка модели

Чтобы иметь возможность создать движение ИИ для нашего врага, нам нужно добавить компонент Nav Mesh Agent к нашему игровому объекту Knight. Единственный параметр, который я собираюсь изменить, — это скорость, которую я установил на 2.

На этом этапе мы можем удалить наш старый игровой объект противника. Нам это больше не нужно.

Далее нам нужно создать NavMesh для нашего врага.

Нажмите на панель навигации рядом с инспектором.

Если его там нет, нажмите Окно › Навигация, чтобы открыть панель.

На вкладке Bake просто нажмите Bake, чтобы создать NavMesh. Сейчас я не собираюсь создавать что-то особенное для нашего персонажа.

Когда мы закончим, у нас должно получиться что-то подобное, если мы покажем созданную нами навигацию.

Убедитесь, что родительский игровой объект environment установлен как статический!

Создание скрипта

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

Для этого я создал скрипт EnemyMovement и прикрепил его к нашему рыцарю.

Вот как это выглядит сейчас:

using UnityEngine;
using UnityEngine.AI;
public class EnemyMovement : MonoBehaviour
{
    private NavMeshAgent _nav;
    private Transform _player;
	void Start ()
	{
	    _nav = GetComponent<NavMeshAgent>();
	    _player = GameObject.FindGameObjectWithTag("Player").transform;
	}
	
	void Update ()
	{
	    _nav.SetDestination(_player.position);
	}
}

Это довольно просто прямо сейчас:

  • Мы получаем наш игровой объект GameObject и компонент Nav Mesh Agent.
  • Настраиваем Nav Agent на преследование нашего игрока

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

После этого мы можем играть в игру, и мы видим, что наш враг-рыцарь будет преследовать нас.

Использование анимации атаки

Прямо сейчас Рыцарь будет бегать вокруг нас. Но как заставить его делать анимацию атаки?

Первое, что нам нужно сделать, это прикрепить компонент capsule collider к нашему игровому объекту рыцаря и выполнить следующие настройки:

  • Является ли триггер отмечен флажком
  • Центр Y равно 1
  • Радиус Y равен 1,5.
  • Высота Y равна 1

Подобно тому, что мы делали в Survival Shooter, когда наш Рыцарь приближается к нам, мы переключаемся на анимацию атаки, которая наносит урон игроку.

Когда наш новый капсульный коллайдер вступит в контакт с игроком, мы добавим логику в наш аниматор, чтобы начать анимацию атаки.

Во-первых, мы создадим новый скрипт под названием EnemyAttack и прикрепим его к нашему рыцарю.

Вот как это выглядит:

using UnityEngine;
using System.Collections;

public class EnemyAttack : MonoBehaviour
{
    Animator _animator;
    GameObject _player;
    void Awake()
    {
        _player = GameObject.FindGameObjectWithTag("Player");
        _animator = GetComponent<Animator>();
    }
    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject == _player)
        {
            _animator.SetBool("IsNearPlayer", true);
        }
    }
    void OnTriggerExit(Collider other)
    {
        if (other.gameObject == _player)
        {
            _animator.SetBool("IsNearPlayer", false);
        }
    }
}

Логика для этого аналогична тому, что мы видели в Survival Shooter. Когда наш коллайдер сработает, мы установим для нашего «IsNearPlayer» значение true, чтобы запустить анимацию атаки, и когда наш игрок покинет диапазон срабатывания, Рыцарь перестанет атаковать.

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

Обнаружение анимации атаки

Добавление меш-коллайдера

Итак, теперь Рыцарь начнет анимацию атаки. Вы можете заметить, что с игроком ничего не происходит.

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

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

Для этого мы прикрепим к врагу Mesh Collider.

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

Убедитесь, что мы присоединили меш body, который использует наш Рыцарь, к нашему Mesh Collider.

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

Добавление события к нашей анимации атаки

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

Конкретно я хочу сделать так, чтобы при атаке Рыцаря, если они столкнутся с игроком, мы получили урон.

Для этого мы собираемся сделать что-то похожее на то, что мы делали в туториале Survival Shooter. Мы собираемся добавить событие внутри нашей анимации для вызова функции в нашем скрипте.

У нас есть 2 способа сделать это.

  1. Создаем Анимационное событие на импортированных клипах из модели
  2. Добавляем Событие анимации во вкладку «Анимация из анимационного клипа

Поскольку наша модель рыцаря не имеет анимации, которую мы добавили, мы собираемся добавить наше событие вторым способом.

Мы хотим отредактировать наш анимационный клип Attack1 из пакета Brute Warrior Mecanim. на вкладке Animator

Выбрав наш Knight Animator Controller, щелкните Attack1 в Animator и выберите вкладку Animation, чтобы открыть ее.

Если какая-либо из этих вкладок еще не открыта в вашем проекте, вы можете открыть их, перейдя в Windows и выбрав их, чтобы поместить в свой проект.

Теперь в этот момент мы столкнемся с проблемой. Наша анимация Attack1 доступна только для чтения, и мы не можем ее редактировать.

Что мы делаем?

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

Вот что мы собираемся сделать. Найдите Attack1 и нажмите Ctrl + D, чтобы продублировать наш клип. Я собираюсь переименовать его в Knight Attack и переместить в папку animations, которую я создал в корневом каталоге проекта.

Вернувшись на вкладку Animator для Knight Animator Controller, я собираюсь переключить состояние Attack1, чтобы вместо этого использовать новый анимационный клип Knight Attack. предыдущего.

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

Для этого я перетащил вкладку Анимация и закрепил ее почти в любом другом месте окна, например так:

Выберите наш объект «Рыцарь» в иерархии игры, и вы увидите, что на вкладке анимация теперь можно нажать кнопку воспроизведения.

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

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

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

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

Затем нам нужно нажать маленькую кнопку + прямо под 16, чтобы создать новое событие. Перетащите это событие в кадр 16.

Из-под Инспектора мы можем выбрать функцию из скриптов, прикрепленных к игре. Сейчас у нас ничего нет, кроме OnTrigger().

А пока давайте создадим пустую функцию Attack() в нашем скрипте EnemyAttack, чтобы мы могли использовать:

using UnityEngine;
using System.Collections;

public class EnemyAttack : MonoBehaviour
{
    Animator _animator;
    GameObject _player;
    void Awake()
    {
        _player = GameObject.FindGameObjectWithTag("Player");
        _animator = GetComponent<Animator>();
    }
    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject == _player)
        {
            _animator.SetBool("IsNearPlayer", true);
        }
    }
    void OnTriggerExit(Collider other)
    {
        if (other.gameObject == _player)
        {
            _animator.SetBool("IsNearPlayer", false);
        }
    }

    void Attack()
    {
        
    }
}

Все, что я сделал, это добавил Attack().

Теперь, когда у нас есть этот код, нам, возможно, придется повторно выбрать анимацию для отображения новой функции, но когда вы закончите, вы сможете увидеть Attack(), и теперь у нас должно быть что-то вроде этого:

Обновление нашего скрипта EnemyAttack

Итак, теперь, когда у нас, наконец, есть все для настройки нашего персонажа, пришло время приступить к написанию кода.

Итак, вернемся к нашему скрипту EnemyAttack, и вот что у нас есть:

using UnityEngine;
using System.Collections;

public class EnemyAttack : MonoBehaviour
{
    private Animator _animator;
    private GameObject _player;
    private bool _collidedWithPlayer;
    void Awake()
    {
        _player = GameObject.FindGameObjectWithTag("Player");
        _animator = GetComponent<Animator>();
    }
    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject == _player)
        {
            _animator.SetBool("IsNearPlayer", true);
        }
        print("enter trigger with _player");
    }
    void OnCollisionEnter(Collision other)
    {
        if (other.gameObject == _player)
        {
            _collidedWithPlayer = true;
        }
        print("enter collided with _player");
    }
    void OnCollisionExit(Collision other)
    {
        if (other.gameObject == _player)
        {
            _collidedWithPlayer = false;
        }
        print("exit collided with _player");
    }
    void OnTriggerExit(Collider other)
    {
        if (other.gameObject == _player)
        {
            _animator.SetBool("IsNearPlayer", false);
        }
        print("exit trigger with _player");
    }
    void Attack()
    {
        if (_collidedWithPlayer)
        {
            print("player has been hit");
        }
    }
}

Вот что я сделал:

  1. Добавлены функции OnCollisionExit() и OnCollisionEnter(), чтобы определять, когда наш Mesh Collider вступает в контакт с нашим игроком.
  2. Как только это произойдет, мы устанавливаем логическое значение, указывающее, что мы столкнулись с врагом.
  3. Затем, когда проигрывается анимация атаки, точно в 16 кадре, мы вызываем Attack(). Если мы все еще находимся в контакте с сеткой коллайдером, наш игрок будет поражен. В противном случае мы успешно увернемся от врага.

И это все!

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

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

Другие вещи, которые мы могли бы сделать, но не сделали:

  1. Сделано так, что если мы когда-нибудь соприкоснемся с врагом, атакующим или нет, мы получим урон.
  2. Создал анимационное событие в начале Knight Attack и установил какое-то логическое значение _isAttacking равным true, а затем в нашем Update(), если враг атакует, и мы находимся в контакте с ним, игрок получает урон, затем установите для _isAttacking значение false, чтобы нас больше не ударили в том же цикле анимации.

Вывод

И это на 12 день! На самом деле это заняло намного больше времени, чем я думал!

Первоначально я думал, что это будет просто применение Nav Mesh Agent, как мы это делали в игре Survivor Shooter, однако, когда я начал думать об анимациях атаки, все стало сложнее, и я потратил много времени, пытаясь понять, как наносить урон. игрок ТОЛЬКО во время анимации атаки.

Завтра я собираюсь обновить PlayerShootingController, чтобы иметь возможность стрелять в нашего врага-рыцаря.

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

Оригинал День 12.

Посетите главную страницу 100 Days of Unity VR Development.

Посетите нашу домашнюю страницу!