Поиск пути с использованием NavMesh

Персонажи в игре часто нуждаются в том, чтобы обходить препятствия на игровом уровне. Так как это очень распространенная ситуация в играх, Unity предоставляет встроенное решение, которое называется NavMesh. В этом уроке вы создадите движение с нажатием на мышь используя инструмент NavMesh.

Версия: C# 7.3, Unity 2020.1, Unity

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

В этом уроке вы узнаете:

  • Подходы, которые используются в играх для поиска пути
  • Как использовать технологию поиска пути Unity NavMesh
  • Способы настройки NavMesh для навигации

Чтобы получить некоторую практику, вы создадите поиск пути, используя инструмент NavMesh и перемещение, которое будет использовать нажатие кнопки мыши. Скачайте файлы проекта вверху страницы нажав на кнопку «Скачать материалы урока». Пример проекта использует версию Unity 2020.1.17 и предоставляет сцену, поэтому вы сможете сосредоточиться на функции поиска пути.

Примечание. Это урок среднего уровня, который предполагает, что вы знакомы с основами работы в Unity. Если вы новичок в Unity, начните с урока
Введение в Unity: начало работы.

Поиск пути в играх

Персонажи с ИИ в играх должны найти лучший путь, чтобы добраться до места назначения. Вы можете выбрать один из нескольких алгоритмов для поиска пути, но A* — отличный выбор во всех, кроме некоторых определенных ситуаций.

A* (алгоритм «A-звездочка»)

Алгоритм «A*» обычно используется для поиска пути во всех видах вычислительных окружений — он не специальный для Unity. На этом сайте есть другая статья по «A*», в которой алгоритм обсуждается в контексте разработки для iOS. Для более глубокого изучения следующая статья предлагает иллюстрированное интерактивное руководство по алгоритму. А вот это руководство объясняет шаги независимо от какого-либо конкретного технологического стека.

Этот урок не объяснит подробно алгоритм «A*», так как вы можете прочитать прекрасные объяснения, на которые указаны ссылки. Вместо этого, правильный подход изучить поиск пути в редакторе Unity!

Сетки навигации

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

Игры чаще всего представляют собой граф поиска пути с сеткой. То есть, как и в 3D-модели, граф, используемый для поиска пути, может быть представлен вершинами, которые соединены ребрами. Подобная сетка называется Navigation Mesh (сетка навигации), поэтому технология Unity для этого имеет сокращенное название NavMesh.

Unity имеет встроенную базовую функциональность NavMesh, но редактор обеспечивает более надежную генерацию сетки и поиск пути через проект с открытым исходным кодом под названием NavMesh Components. Когда вы настраиваете свои собственные проекты, вы можете загрузить эти компоненты из репозитория GitHub. Но они уже включены в стартовый проект. Теперь пора поговорить о том, как использовать эти компоненты.

Приступая к работе

Чтобы научиться пользоваться компонентами NavMesh, вы разработаете простую демонстрацию перемещения по принципу «point-and-click». Персонаж переместится в место, на которое щелкнули мышью, как в приключенческой игре. Благодаря мощности NavMesh персонаж даже будет знать, как обходить препятствия.

Вашему персонажу нужна сцена, по которой можно бегать, и некоторые препятствия, по которым можно перемещаться. К счастью, в стартовом проекте они есть. Перейдите в Assets/RW/Scenes и откройте SampleScene. В этой сцене есть просто набор блоков для стен и пола, а также капсула, представляющая игрока.

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

Создание NavMesh

Сначала создайте пустой игровой объект и назовите его NavMesh. Навигационная сетка будет сгенерирована и прикреплена к этому объекту. Теперь добавьте к этому объекту компонент NavMeshSurface. В Инспекторе теперь есть набор настроек навигации, а также пара кнопок для управления генерацией сетки.

Вот и все — вы готовы создать свою первую сетку! Нажмите на кнопку Bake, и Unity сгенерирует навигационную сетку с настройками по умолчанию.

Вид сцены с запеченной сеткой navmesh в Unity
Первая запеченная сетка navmesh в Unity

Понимание NavMeshSurface

Что произошло? Базовый код довольно сложен, но понять основные этапы несложно. NavMeshSurface сначала решает, какой путь вверх, используя вращение своего собственного игрового объекта. Затем он определяет поверхности сцены, обращенные в этом направлении. (В данном случае они все плоские, но NavMeshSurface также работает с ландшафтом и другими нерегулярными сетками.)

Затем система NavMesh должна определить, по каким частям каждой поверхности можно ходить. Игровые персонажи бывают самых разных форм и размеров. Например, области, по которым может пройти маленький кот-персонаж, будут отличаться от тех, по которым может пройти человек или гигантский робот. Здесь появляется диаграмма в верхней части NavMeshSurface.

Диаграмма настроек агента, показанная в NavMeshSurface редактора Unity
Это может выглядеть, как записи из класса математики, но это просто настройки агента NavMesh

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

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

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

Настройка NavMesh со слоями

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

В NavMeshSurface есть раскрывающееся меню Include Layers. Если вы отмените выбор слоя в этом раскрывающемся списке, система создания сетки игнорирует объекты на этом слое. Выполните следующие действия, чтобы поместить игрока на новый слой и затем исключить этот слой из сетки:

  1. Выберите игровой объект Player в окне Hierarchy.
  2. В раскрывающемся списке Layer выберите Add Layer.
  3. В появившемся виде управления слоями введите имя Player в первой доступной строке.
  4. Снова выберите игрока. В раскрывающемся списке Layer измените его слой на только что созданный слой Player. (В качестве дополнительной задачи скажите себе «уровень игрока» пять раз быстрее).
  5. Появится всплывающее окно с вопросом, следует ли назначать дочерние объекты этому слою. Выберите Yes, change children (да, смените объекты-потомки).
  6. Выберите NavMesh.
  7. В Инспекторе откройте раскрывающееся меню Include Layers и нажмите Player, чтобы отменить выбор.
Раскрывающийся список выбора слоев в Unity
Создайте и затем назначьте слой для игрока из этого раскрывающегося списка.

Теперь Unity поймет, что игрок не является частью декораций. Нажмите на кнопку Bake, чтобы восстановить сетку навигации. Больше никаких пустых отверстий!

Удаление объекта из NavMesh

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

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

  1. Посмотрите на потомков Maze и выберите Corner.
  2. Добавьте компонент NavMeshModifier.
  3. В настройках компонента нажмите на Override Area.
  4. Измените Area Type на Not Walkable.
Настройки изменения Area компонента NavMeshModifier в Unity
Изменение настроек параметра Area компонента NavMeshModifier

Теперь Unity понимает, что Corner — это препятствие, а не поверхность, по которой можно ходить. Нажмите на NavMesh и снова запеките сетку навигации. Задача решена!

Угловой блок без NavMesh в Unity
Снова обычная стена в NavMesh.

С этими параметрами вы правильно настроили навигацию. Вы готовы побегать по лабиринту!

Перемещение персонажа

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

Настройка компонента NavMeshAgent

Для начала выберите игрока и добавьте компонент NavMeshAgent.

Настройки по умолчанию для движения агента немного медленные, но вы можете настроить их, чтобы они были более быстрыми. На NavMeshAgent увеличьте параметр Speed до 5, Angular Speed до 360 и Acceleration до 10.

Настройки управления в компоненте NavMeshAgent редактора Unity
Настройки управления в компоненте NavMeshAgent

Теперь вам просто нужен скрипт для установки цели NavMeshAgent, а NavMesh позаботится обо всем остальном!

Установка цели для NavMeshAgent

Создайте новый скрипт C# с именем PlayerMovement.cs в каталоге Assets/RW/Scripts. Добавьте это требование в самом верху класса, над определением класса:

using UnityEngine.AI;

Эта библиотека делает доступными классы NavMesh.

Затем объявите эти две переменные в верхней части класса:

private Camera cam;
private NavMeshAgent agent;

Добавьте эти строчки кода в метод Start:

cam = Camera.main;
agent = GetComponent<NavMeshAgent>();

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

Добавьте в функцию Update следующее:

if (Input.GetMouseButtonDown(0))
{
    Ray ray = cam.ScreenPointToRay(Input.mousePosition);

}

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

Затем добавьте эти строчки внутри оператора if после создания луча:

if (Physics.Raycast(ray, out RaycastHit hit))
{
    agent.SetDestination(hit.point);
}

Этот код отправляет луч в игровой мир и проверяет, попадает ли он во что-нибудь. (Обратите внимание, что луч будет поражать только объекты, у которых есть коллайдеры.) Если луч действительно попадает во что-то, он сохранит местоположение попадания в hit.point. Затем вы можете просто указать это местоположение агенту в качестве цели.

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

Улучшение навигационной сетки

Теперь, когда персонаж успешно перемещается по лабиринту, вы можете сделать навигационную сетку немного интересной, указав areas (области навигации) и costs (затраты). Фактически вы уже настроили одну область навигации, установив для углового блока значение Not Walkable. Но вы можете сделать гораздо больше, чем просто включать и выключать области.

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

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

Назначение областей навигации

Сначала добавьте новую область в систему навигации. Выберите Window > AI > Navigation, чтобы открыть настройки навигации по проекту. Затем выберите вкладку Areas, чтобы увидеть все области навигации, определенные в этом проекте. Введите название Water в первом доступном ряду и установите для него стоимость 5.

Вкладка Areas в окне Navigation редактора Unity
Список областей и затрат на вкладке Areas в окне Navigation.

Вернитесь к просмотру сцены. Посмотрите на объекты-потомки Maze и выберите Water. (Возможно, вам потребуется выбрать вкладку Inspector, чтобы снова увидеть ее, если она находится за навигацией.) Добавьте компонент NavMeshModifier, включите Override Area и измените Area Type на Water. Затем запекаем сетку еще раз.

Сетка навигации с фиолетовой областю в редакторе Unity
Сетка навигации с водной областью, показанной фиолетовым цветом

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

Пример поиска пути NavMesh, который избегает области навигации с высокой стоимостью в редакторе Unity
Поместите игрока в верхний правый угол, затем нажмите там, где находится знак «x».

Куда двигаться дальше?

Загрузите готовые файлы проекта, нажав кнопку «Скачать материалы урока» вверху этого урока.

Теперь вы знаете основы поиска пути с помощью NavMesh в Unity! Помните о трех основных компонентах: NavMeshSurface, NavMeshModifier и NavMeshAgent.

С системой NavMesh вы можете сделать гораздо больше. Вы можете определять различные типы агентов, используя настройки NavMeshAgent, создавать персонажей, которые ходят по стенам или потолку, или исследовать связывание нескольких сеток вместе. Или вы можете попробовать следующую генерацию среды выполнения. «Генерация среды выполнения» относится к созданию навигационной сетки во время работы игры, а не к ее предварительному запеканию, как вы это делали в этом руководстве.

Хотя NavMesh бесплатен и хорошо работает, многие разработчики предпочитают A * Pathfinding Project. Если вам требуется поиск пути для вашей игры, проверьте этот алгоритм тоже.

Надеюсь, вам понравился данный урок! Если у вас есть какие-либо вопросы, комментарии или предложения, не стесняйтесь оставлять их в комментариях.

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

Автор перевода: Jean Winters

Источник: Pathfinding With NavMesh: Getting Started

Смотрите также:

Как программисту создать графику для игрыКак программисту создать игровую графику (или как подделать ее в стиле игрового художника)


Unity: Введение в юнит-тестированиеUnity: Введение в юнит-тестирование


Комментариев: 2 на “Поиск пути с использованием NavMesh

  1. Почему-то в настройках navmeshsurface другие не такие параметры, и не отображаются данные с цифрами как в уроке.

Комментировать

Почта не публикуется.Обязательные поля отмечены *