1. Documentation

Nav3D Документация

1.1 Getting Started

  • Все классы ассета, необходимые вам для работы, содержатся в пространстве имен Nav3D.API.
  • Во время функционирования Nav3D создаст на сцене несколько MonoBehaviour объектов, необходимыми ему для внутреннего использования.

Эти игровые объекты создаются с флагом DontDestroyOnLoad. Сохранение менеджеров при загрузке другой сцены может быть полезно, если, например, вы используете отдельную сцену загрузки, в которой вы инициализируете Nav3D.

  • Перемещение агентов (Nav3DAgent) выполняется внутри события FixedUpdate
  • Все длительные вычислительные операции производятся средствами CPU вне главного потока.
  • Все колбеки, предоставленные в API Nav3D исполняются в главном потоке Unity.

1.1.1 Nav3DInitializer

Для использования Nav3D в игровом режиме, надо его проинициализировать.

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

 

На сцене появится следующий игровой объект:

  • Init On Awake – Оставьте включенным, чтобы Nav3D выполнил инициализацию в игровом режиме при выполнении события Awake.
  • Dispose On Destroy – Оставьте включенным, чтобы Nav3D почистил все свои выделенные сущности при уничтожении сцены. Если отключите, то сущности Nav3D останутся и продолжат функционирование и на следующей сцене, которая будет загружена.
  • Min Bucket Size – Минимальный размер ячеек навигационного графа. Должен быть положительным ненулевым числом. Тут поясним чуть подробнее. По сути этот параметр позволяет настраивать уровень детализации навигационного графа. Чем меньше значение параметра - тем выше детализация графа.

Все игровые агенты, которые будут искать путь при помощи Nav3D имеют свой размер, определяемый радиусом, который вы им задаете при создании. (Об этом будет рассказано чуть позже). Минимальный размер ячейки должен быть равен максимальному из радиусов агентов, это будет гарантировать, что агент сможет пройти по любому пути на графе. Если на сцене будут находиться агенты, радиус которых превосходит минимальный размер ячейки графа поиска, то возможны ситуации, когда агенты будут задевать препятствия на сцене. В будущих версиях Nav3D будет реализован поиск пути с учетом размера агента.

*Если параметры Init On Awake и Dispose On Destroy будут отключены, то инициализация и освобождение ресурсов Nav3D будет лежать на вашей ответственности. Для инициализации вам надо будет вызвать метод Nav3DInitializer.Init(), для утилизации - Nav3DInitializer.Utilize(). Значение параметра Min Bucket Size также может быть установлено через код, для этого используйте свойство MinBucketSize.

1.1.2 Nav3DManager

Nav3DManager - вспомогательный static класс. Может быть полезен для проверки факта инициализации Nav3D и/или выполнения определенных действий в момент инициализации.

Содержит:

  • Свойство bool Inited, которое показывает, был ли Nav3D уже проинициализирован.
  • Событие event Action OnNav3DInit. Оно срабатывает сразу после инициализации Nav3D. Подписавшись на него вы сможете выполнить какие-либо действия с Nav3D сразу после инициализации. При подписке на ивент ваш делегат сработает моментально, в случае если инициализация уже была произведена ранее.

В случае попытки использования публичных методов из классов пространства Nav3D.API до инициализации Nav3D будет выброшено исключение Nav3DManagerNotInitializedException. Любые действия с сущностями Nav3D можно производить только после его инициализации.

1.2 Dealing with Obstacles

Nav3D позволяет использовать в качестве препятствий игровые объекты на сцене с компонентом MeshFilter с назначенным Mesh, либо с компонентом Terrain.

*Для препятствий, использующих MeshFilter, убедитесь, что для mesh настройках импорта включена опция “Read/Write Enabled”.

1.2.1 Nav3DObstacle

Для управления препятствиями на сцене используется компонент Nav3DObstacle. Добавьте его на игровой объект, который вы хотите сделать препятствием на сцене.

 

Вы увидите инспектор компонента:

В нем есть следующие настройки:

  • Выбор режима обработки препятствия (обработка препятствия должна выполняться после инициализации Nav3D, чтобы оно учитывалось при поиске пути и, агенты могли его обойти во время движения):
    • Runtime - препятствие будет обработано прямо во время игрового режима после инициализации Nav3D. Это может быть полезно, если препятствие генерируется во время игрового режима.
    • Load from binary - препятствие будет загружено из заранее заготовленного бинарного файла. Во многих случаях обстановка игровой сцены известна заранее, еще до загрузки сцены и инициализации Nav3D. В таких случаях всегда имеет смысл запекать препятствия сцены в режиме редактора, а на старте сцены загружать их из бинарного файла. Загрузка из бинарного файла всегда гораздо быстрее, чем обработка препятствия непосредственно в игровом режиме (runtime).
  • Auto add child transforms - если включено, то для всех детей transform дерева будет совершен сбор информации о компонентах MeshFilter и Terrain. И все дети, имеющие данные компоненты, будут учтены при обработке препятствия                      *If you want any child element of an obstacle containing a MeshFilter or Terrain not to be taken into account when constructing the navigation graph, then attach the Nav3DObstacleIgnoreTag component to it.
  • Update automatically on transform change - если включено, то в случае изменения положения, поворота или масштаба transform компонента препятствия, будет выполняться его повторная обработка чтобы актуализировать граф поиска. (Препятствие будет сначала удалено из хранилища препятствий, затем будет повторно обработано и добавлено в него).Данная функция доступна для использования только для препятствий с выбранным Runtime режимом обработки.

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

  • Update min period - как часто должно обновляться препятствие (в секундах) при включенном параметре “Update automatically on transform change”

Для runtime-препятствий возможно их удаление/повторное добавление в хранилище прямо во время игры. Добавление происходит при срабатывании OnEnable(), удаление происходит при срабатывании OnDisable(). Соответственно, любое runtime-препятствие, созданное на сцене во время игры будет обработано и добавлено в хранилище. Если вы удалите или отключите игровой объект препятствия, то оно удалится из хранилища. Повторное включение препятствия вновь добавить его в хранилище.

1.2.2 Nav3DObstacleLoader

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

 

На сцене появится следующий игровой объект:

 

Для всех препятствий на сцене, которые вы хотите запечь, выберите режим обработки Load from binary

 

Далее в инспекторе компонента Nav3DObstacleLoader вы сможете увидеть список всех игровых объектов с компонентом Nav3DObstacle, подлежащих запеканию.

 

Нажмите кнопку “Bake and serialize obstacles” и выберите папку в проекте для сохранения бинарного файла с запеченными данными о препятствиях на сцене.

После успешного запекания инспектор компонента Nav3DObstacleLoader примет такой вид:

 

Инспектор успешно сериализованных Nav3DObstacle будет иметь такой вид:

 

Теперь в игровом режиме при инициализации Nav3D навигационные графы всех запеченных препятствиях на сцене будут загружены из бинарного файла.

1.2.3 Deeper dive into obstacles

  • Как уже было описано выше, для того чтобы препятствие было учтено при поиске пути, необходимо навесить на его игровой объект компонент Nav3DObstacle
  • В Nav3D существует хранилище препятствий, которое используется при поиске пути. В нем хранятся навигационные графы препятствий на сцене.
  • Все операции с препятствиями (добавление или удаление) ставятся в очередь исполнения и производятся последовательно в отдельном потоке.
  • Под обработкой препятствия подразумевается построение графа проходимости (он же навигационный граф) для каждого препятствия, либо для группы препятствий, а затем добавление этого графа в хранилище препятствий.

Графы проходимости используются при поиске пути на сцене.

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

1.3 Particular resolution regions

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

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

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

Для этого мы реализовали объект Nav3DParticularResolutionRegion, который позволяет задавать регион в пространстве, в котором при построении будет использован специфический минимальный размер ячеек графа поиска.

1.3.1 Nav3DParticularResolutionRegion

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

  • Все классы ассета, необходимые вам для работы, содержатся в пространстве имен Nav3D.API.

Задайте появившемуся игровому объекту нужное положение на сцене. Далее в инспекторе компонента Nav3DParticularResolutionRegion настройте размеры области, а также укажите нужный минимальный размер ячеек графа поиска . Для визуализации региона включите отображение Gizmos в окне сцены.

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

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

1.3.2 Nav3DParticularResolutionRegion : Some points

  • Использование регионов разрешения подразумевается для решения задач связанных с проходимостью графа в тесных областях пространства, таких как например узкие тоннели, маленькие отверстия, сквозь которые должна быть обеспечена возможность поиска пути. Но также в таких регионах можно задавать размер минимальной ячейки графа поиска больший, чем размер установленный на всей сцене. Это может быть полезно, когда вы наоборот хотите избежать избыточной детализации графа поиска для некоторых препятствий. Избегание избыточно высокого разрешения графа поиска благоприятно скажется на общей производительности при поиске пути.
  • Октодерево является иерархической структурой, в которой размеры всех ячеек всегда кратны размеру друг друга и от уровня к уровню размер меняется с множителем два (каждый следующий уровень содержит ячейки в два раза меньше чем в предыдущем). Так что вы можете указать лишь желаемый минимальный размер ячейки. На деле реальный минимальный размер ячейки будет вычислен относительно размера указанного при инициализации Nav3D. Если например при инициализации минимальный размер был указан как 0.4, а вы указываете желаемый минимальный размер как 0.3, то фактически будет выбран размер 0.2, поскольку он должен быть уровнем ниже и получен из 0.4 делением на 2. Если же вы выберете желаемый размер как 0.7, то фактический размер будет равен 0.8, поскольку он должен быть уровнем выше и получен умножением на 2.
  • В случае если препятствие находится внутри пересечения нескольких регионов с разным минимальным размером ячеек, при обработке в качестве минимальной ячейки будет выбрано наименьшее значение из значений регионов.
  • Рекомендуем использовать регионы в качестве статических объектов, находящихся на сцене в момент обработки всех препятствий. Операция добавления/удаления региона во время игры повлечет повторную обработку тех runtime-препятствий, которые пересекаются регионом. Static-препятствия никак не отреагируют на удаление/добавление региона. Инициализация и деинициализация региона происходит по событиям OnEnable() и соответственно DoDisable() в Nav3DParticularResolutionRegion. Так что для инициализации региона можно создать его префаб на сцене, для деинициализации - сделать неактивным его GameObject, либо уничтожить его.

1.4 Path object

Класс Path предназначен для работы с траекториями в пространстве.

Как правило, объекты этого типа используются агентами(Nav3DAgent)при выполнении поиска пути. Но вы также можете использовать Path , чтобы найти путь для своих собственных целей.

Разумеется, работа с объектами Path возможна только после инициализации Nav3D. Подробнее читайте в разделе “Инициализация Nav3D”.

1.4.1 Nav3DPathfindingManager

Данный класс содержит инструменты для работы с поиском путей на сцене.

Для достижения необходимого уровня производительности при использовании Nav3D могут быть полезны следующие свойства:

  • public static int CurrentPathfindingTasksCount – показывает сколько параллельных задач поиска пути активны на данный момент;
  • public static int MaxPathfindingTasks – позволяет устанавливать ограничение на максимальное количество задач поиска пути, работающих единовременно. По умолчанию данное свойство установлено на значение Environment.ProcessorCount - 1

Также имеется метод public static Path PrefetchPath(Vector3 _PointA, Vector3 _PointB) для создания экземпляра Path. О его применении сказано ниже.

1.4.2 Pathfinding

Для поиска пути между точками A и B необходимо:

Получить объект типа Path , вызвав метод

				
					Nav3DPathfindingManager.PrefetchPath(Vector3 _PointA, Vector3 _PointB)
				
			

Данный метод лишь создаст экземпляр Path и зарегистрирует его в Nav3D, но поиск пути осуществлен не будет.

Для созданного объекта Path вызовите метод

				
					UpdatePath(
                Vector3? _Start = null,
            	Vector3? _Goal = null,
            	int? _Timeout = null,
            	bool? _Smooth = null,
            	Action _OnSuccess = null,
            	Action<PathfindingError> _OnFail = null
)
				
			

Значения перегруженных параметров:

  • Vector3? _Start точка начала пути. Параметр необходимо передавать если вы хотите поменять точку начала пути.
  • Vector3? _Goal конечная точка пути. Параметр необходимо передавать если вы хотите поменять конечную точку пути.
  • int? _Timeout максимальное время поиска пути. В случае, если поиск пути длится дольше, произойдет выполнение колбека _OnFail, если он не равен null.
  • bool? _Smooth = null необходимо ли сглаживать найденный путь.
  • Action _OnSuccess null - колбек, который вызовется после успешного нахождения пути
  • Action _OnFail колбек, который вызовется в случае, если путь не удалось найти по какой-либо причине.

1.4.3 PathfindingError

Класс, экземпляр которого возвращается в колбек _OnFail метода Path.UpdatePath() .

Содержит два свойства:

  • public PathfindingResultCode Reason причина неудачи поиска пути
    Возможные значения свойства:
ValueОписание
SUCCEEDEDПоиск пути завершился успешно.
PATH_DOES_NOT_EXISTМежду точками не существует пути. Это возможно если хотя бы одна из точек окружена препятствиями со всех сторон.
TIMEOUTПоиск пути занял больше времени чем разрешено и был прерван.
CANCELEDПоиск пути был завершен по указанию пользователя, или по внутренней логике Nav3D.
START_POINT_INSIDE_OBSTACLEПоиск пути не удался, потому что начальная точка пути находится внутри препятствия
GOAL_POINT_INSIDE_OBSTACLEПоиск пути не удался, потому что конечная точка пути находится внутри препятствия.
UNKNOWNВнутренняя ошибка.
  • public string Msg сообщение с более подробной информацией об ошибке.

1.4.4 Path : Properties

  • public Vector3[] Trajectory { get; } - траектория найденного пути. Используйте это свойство для получения последнего найденного пути. Значение является верным, если метод UpdatePath был вызван и завершился успешно и никакие прочие свойства объекта Path не менялись после этого. Используйте свойство IsValid для проверки того, что значение верно.
  • public Vector3[] TrajectoryOriginal {get;} исходная найденная траектория.
  • public Vector3[] TrajectoryOptimized {get;} оптимизированная найденная траектория.
  • public Vector3[] TrajectorySmoothed { get; } сглаженная траектория найденного пути. Значение будет отличаться от TrajectoryOptimized, если свойство Smooth имеет значение true.
  • public Bounds Bounds { get; } объем пространства, который занят найденным путем.
  • public Vector3 Start { get; set; } точка начала пути.
  • public Vector3 Goal { get; set; } конечная точка пути.
  • public int SmoothRatio { get; set; } - количество проходов сглаживания по отношению к минимальному размеру ячейки графа поиска, по умолчанию установлено как 3. Не рекомендуем увеличивать это значение без необходимости, так как это повлечет рост времени поиска пути, в случае если сглаживание включено (свойство Smooth == true).
  • public int Timeout { get; set; } - максимальное время поиска пути. В случае, если поиск пути длится дольше, произойдет выполнение колбека Action<PathfindingError> _OnFail , если он был передан в UpdatePath().
  • public bool Smooth { get; set; } нужно ли производить сглаживание пути.
  • public bool IsValid { get; } - является ли текущее значение Trajectory не null и верно ли оно для текущих значений остальных свойств. Например если вы вызвали UpdatePath(), а после его успешного завершения изменили значение свойства Start, то путь уже не будет являться актуальным и IsValid будет равно false.
  • public bool IsPathUpdating { get; } находится ли экземпляр Path в процессе поиска пути.

1.4.5 Completing dealing with the Path Object

После того как вы завершили работу с объектом Path, необходимо вызвать метод Path.Dispose() method. This is necessary to remove the path from the obstacle storage. Otherwise, when adding / updating / removing any obstacle, the path will be checked for the need to update, which will take up computing resources.

1.4.6 Usage example

Ниже приведен пример использования объекта Path для нахождения пути между А и В

				
					using Nav3D.API;
using System;
using UnityEngine;

class YourGameScenario : MonoBehaviour
{
    private void Start()
    {
        Nav3DManager.InitNav3D(1f);

        Vector3 a = new Vector3(-10, 10, 10);
        Vector3 b = new Vector3(-10, 10, 10);

        FindPath(a, b, PrintPath);
    }

    //your method for pathfinding from A to B
    void FindPath(Vector3 _A, Vector3 _B, Action<Vector3[]> _OnPathFound)
    {
        //create Path instance
        Path path = Nav3DPathfindingManager.PrefetchPath(_A, _B);

        //perform pathfinding from _A to _B
        path.UpdatePath(_OnSuccess: () =>
        {
            //notify that path was found
            UnityEngine.Debug.Log("Path successfully found!");

            //invoke callback
            _OnPathFound(path.Trajectory);

            //finish work with Path instance
            path.Dispose();
        });
    }

    void PrintPath(Vector3[] _Path)
    {
        for (int i = 0; i < _Path.Length - 1; i++)
            UnityEngine.Debug.LogError($"Path point {i}: {_Path[i]}");
    }
}
				
			

1.5 Agents

Nav3DAgent то основной класс, осуществляющий движение игровых юнитов в пространстве игровой сцены.

Nav3DAgent Использование публичных методов возможно только после инициализации Nav3D. Подробнее читайте в разделе Nav3DInitializer.

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

Для использования Nav3DAgentнеобходимо настроить его поведение. Это возможно сделать с помощью использования класса Nav3DAgentDescription .

1.5.1 Nav3DAgentDescription

				
					Nav3DAgentDescription
				
			

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

После создания описания в инспекторе вы увидите следующий список групп параметров:

Кнопка Set default parameters ускорит настройку всех параметров, выставив их значения по умолчанию.

Приведем пояснение назначения параметров и их допустимые величины.

Behavior

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Behavior Type
Behavior Type
enum BehaviorType
DEFAULT, YIELDING, INDIFFERENT

Описывает тип поведения агента.

  • Агент может вести себя стандартным образом (DEFAULT), т.е.искать путь, за тем следовать по нему, или выполнять любые другие указания пользователя. Поведение агента будет полностью определяться его описанием.
  • Агент может иметь уступающее поведение (YIELDING). Он не сможет выполнять команды пользователя и не будет иметь собственных задач.
    (При попытке вызова методов движения к цели будет выброшено исключение.) Его единственной заботой будет уступать дорогу агентам поблизости. С ними он будет взаимодействовать с помощью механизма Local avoidance, в соответствии со своими параметрами скорости и радиуса.
  • Также агент может быть безразличным (INDIFFERENT).
    Агент не будет делать ничего, просто будет находиться на месте. Другие агенты смогут обходить его при выполнении локального уклонения. Попытка вызова методов следования к цели с таким типом поведения повлечет выброс исключения, по аналогии с YIELDING.
Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Motion Navigation Type
MotionNavigationType
enum MotionNavigationType
GLOBAL, GLOBAL_AND_LOCAL, LOCAL

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

  • Используя глобальный путь (GLOBAL). В таком случае агент выполнит поиск пути и будет двигаться по нему, невзирая на внешние условия (другие агенты, находящиеся слишком близко края препятствий).
  • С использованием только локального уклонения (LOCAL). Агент не будет использовать поиск пути для движения, а будет двигаться к цели по прямой, попутно уклоняясь от других агентов и препятствий, когда они встречаются на курсе движения. Разумеется не всякое препятствие может быть преодолено таким образом.
  • Наконец, агент может осуществлять движение к цели используя поиск пути и локальное уклонение одновременно (GLOBAL_AND_LOCAL). Агент будет двигаться по найденному пути и уклоняться от подошедших близко других агентов, а также находящихся слишком близко препятствий.

Radius

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Radius
Radius
float
>0

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

Speed

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Speed
Speed
float
>0

Скорость движения агента. Можете менять ее в любой момент времени, если потребуется.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Max Speed
Max Speed
float
>Speed

Максимальная скорость агента. Этот параметр нужен для выполнения локального уклонения. В некоторых ситуациях агенту может потребоваться ускориться, чтобы избежать столкновения. Если значение будет слишком велико, то порой агент может двигаться рывками при выполнении локального уклонения. Так что советуем выставлять максимальную скорость чуть больше чем Speed(больше менее чем в полтора раза).

Для вашего удобства в инспекторе имеется два способа установления этого значения. С помощью конкретного значения. Либо с помощью множителя для Speed. Советуем использовать множитель, для того чтобы максимальная скорость зависела от Speed агента. Тогда MaxSpeed будет меняться вслед за изменением Speed

Local avoidance

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
ORCA Tau
ORCATau
float
>0

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

Лучше понять его смысл вам поможет оригинальная статья по ORCA Reciprocal n-body Collision Avoidance. Jur van den Berg, Stephen J. Guy, Ming Lin, and Dinesh Manocha

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Agents considered number limit
Agents considered number limit
bool
true, false

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

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Agents number
ConsideredAgentsNumberLimit
int
>=1

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

Поиск пути

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Pathfinding timeout (ms)
PathfindingTimeout
int
>0

Максимальное время, отведенное на выполнение поиска пути. При превышении этого времени поиск(pathfinding) будет отменен, и будет вызван колбек _OnPathfindingFail.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Smooth the path
SmoothPath
bool
true, false

Нужно ли сглаживать найденный путь.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Samples per min bucket volume
SmoothRatio
int
>0

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

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Auto-update path on stagnant behavior
AutoUpdatePath
bool
true, false

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

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

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Auto-update cooldown (ms)
PathAutoUpdateCooldown
int
>=1

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

Motion

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Target reach distance
TargetReachDistance
float
>=0

Расстояние достижения цели. Когда вы отдаете агенту приказание достичь какой-либо цели, цель будет считаться достигнутой, когда pivot агента совпадет с координатой цели (расстояние между ними будет равным 0). Это может быть неудобно, если целью является объект с мешем, допустим планета в космосе. В таком случае выставьте этот параметр равным сумме радиуса видимой части агента и радиуса планеты. Тогда агент остановится, достигнув поверхности планеты.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Max rotation in degrees per fixed update tick
MaxAgentDegreesRotationPerTick
float
>=0

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

Velocity blending

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

  1. Вектор скорости следования по пути.
  2. Вектор скорости уклонения от агентов.
  3. Вектор скорости уклонения от препятствий.

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

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

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

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

Все веса задаются типом float и должны иметь значение более 0

Соответствующие названия свойств в Nav3DAgentDescription:

  • PathVelocityWeight
  • PathVelocityWeight1
  • PathVelocityWeight2
  • AgentsAvoidanceVelocityWeight
  • AgentsAvoidanceVelocityWeight1
  • AgentsAvoidanceVelocityWeight2
  • ObstacleAvoidanceVelocityWeight
  • ObstacleAvoidanceVelocityWeight1
  • ObstacleAvoidanceVelocityWeight2

Debug

Use Log UseLog bool Whether it is necessary to log agent work processes. true, false
Log records count
LogSize
int
Agent log size.
>0

1.5.2 Nav3DAgent

Как упоминалось выше, вы можете использовать скрипт Nav3DAgent в качестве компонента для вашего игрового объекта и получить к нему доступ через GetComponent() или вы можете создать класс-наследник, который лично мы считаем гораздо более удобным.

Для работы с Nav3DAgent были реализованы следующие публичые методы:
  • Применяет описание к вашему агенту.
				
					public void SetDescription(Nav3DAgentDescription _Description)
				
			
  • Команда двигаться к точке. Любая действующая команда будет прервана, агент начнет движение к точке, в соответствии с параметрами его описания.
				
					public void MoveTo(Vector3 _Point, Action _OnReach = null, Action<PathfindingError> _OnPathfindingFail = null)
				
			
  • Добавить точку следования в очередь. Таким образом может быть добавлено сколько угодно точек следования. Если агент выполняет команду FollowTarget, то она прервется и он начинает выполнять команду следование к точке.
				
					public void MoveToEnqueue(Vector3 _Point, Action _OnReach = null, Action<PathfindingError> _OnPathfindingFail = null)
				
			
  • Команда преследовать движущийся Transform.

				
					public void FollowTarget(
            Transform _Target,
            float _OffsetToleranceUpdate,
            float _DistToReach = 0,
            Action _OnReach = null,
            Action _OnCancel = null,
            Action<PathfindingError> _OnPathfindingFail = null
        )
				
			
  • Transform _Target – Transform, который необходимо преследовать.
  • float _OffsetToleranceUpdate – величина минимального смещения цели, которое приведет к обновлению пути к цели. Если ваш агент имеет параметр Motion Navigation Type описания установленным в значение GLOBAL, или GLOBAL_AND_LOCAL, то каждый раз при смещении преследуемого Transform более чем на _OffsetToleranceUpdate произойдет обновление пути.
  • If your agent has the Motion Navigation Type of the description set to GLOBAL, or GLOBAL_AND_LOCAL, then each time the pursued Transform is offset by more than _OffsetToleranceUpdate , it will cause the path to be recalculated.
  • float _DistToReach – расстояние до цели, приблизившись на которое цель будет считаться достигнутой. Хотим заметить, что в случае когда агенту приходится искать глобальный путь, цель может никогда не быть достигнутой, поскольку достигнув конца пути может оказаться, что цель за прошедшее время сдвинулась. Таким образом условие совпадения координат преследуемой цели и агента может быть труднодостижимым.
  • Action _OnReach – Делегат, который будет выполнен, когда цель будет успешно достигнута.
  • Action _OnCancel – делегат, выполняемый в случае, если ссылка на преследуемый Transform стала null, либо missing.
  • Прерывает выполнение текущей команды.
				
					public void Stop()
				
			
  • Позволяет получить список агентов внутри радиуса _Radius от агента. Вызов этого метода по событию Update, может негативно сказаться на производительности, поэтому рекомендуется не вызывать его слишком часто (например, нескольких раз в секунду должно быть достаточно для любых игровых сценариев).
				
					public List<Nav3DAgent> GetAgentsInRadius(float _Radius, Predicate<Nav3DAgent> _Predicate = null)
				
			
  • Визуализирует агента с помощью Gizmos. Может быть полезно для отладки в Editor
				
					public void Visualize(bool _DrawRadius = true, bool _DrawPath = true, bool _DrawVelocities = true)
				
			

Вы можете найти примеры использования Nav 3D Agent в демонстрационных сценах, расположенных в папке Demo ресурса.

1.5.3 Nav3DAgent : Debug

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

2. Getting Started

  • Все классы ассета, необходимые вам для работы, содержатся в пространстве имен Nav3D.API.
  • Во время функционирования Nav3D создаст на сцене несколько MonoBehaviour объектов, необходимыми ему для внутреннего использования.

Эти игровые объекты создаются с флагом DontDestroyOnLoad. Сохранение менеджеров при загрузке другой сцены может быть полезно, если, например, вы используете отдельную сцену загрузки, в которой вы инициализируете Nav3D.

  • Перемещение агентов (Nav3DAgent) выполняется внутри события FixedUpdate
  • Все длительные вычислительные операции производятся средствами CPU вне главного потока.
  • Все колбеки, предоставленные в API Nav3D исполняются в главном потоке Unity.

2.1 Nav3DInitializer

Для использования Nav3D в игровом режиме, надо его проинициализировать.

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

 

На сцене появится следующий игровой объект:

  • Init On Awake – Оставьте включенным, чтобы Nav3D выполнил инициализацию в игровом режиме при выполнении события Awake.
  • Dispose On Destroy – Оставьте включенным, чтобы Nav3D почистил все свои выделенные сущности при уничтожении сцены. Если отключите, то сущности Nav3D останутся и продолжат функционирование и на следующей сцене, которая будет загружена.
  • Min Bucket Size – Минимальный размер ячеек навигационного графа. Должен быть положительным ненулевым числом. Тут поясним чуть подробнее. По сути этот параметр позволяет настраивать уровень детализации навигационного графа. Чем меньше значение параметра - тем выше детализация графа.

Все игровые агенты, которые будут искать путь при помощи Nav3D имеют свой размер, определяемый радиусом, который вы им задаете при создании. (Об этом будет рассказано чуть позже). Минимальный размер ячейки должен быть равен максимальному из радиусов агентов, это будет гарантировать, что агент сможет пройти по любому пути на графе. Если на сцене будут находиться агенты, радиус которых превосходит минимальный размер ячейки графа поиска, то возможны ситуации, когда агенты будут задевать препятствия на сцене. В будущих версиях Nav3D будет реализован поиск пути с учетом размера агента.

*Если параметры Init On Awake и Dispose On Destroy будут отключены, то инициализация и освобождение ресурсов Nav3D будет лежать на вашей ответственности. Для инициализации вам надо будет вызвать метод Nav3DInitializer.Init(), для утилизации - Nav3DInitializer.Utilize(). Значение параметра Min Bucket Size также может быть установлено через код, для этого используйте свойство MinBucketSize.

2.2 Nav3DManager

Nav3DManager - вспомогательный static класс. Может быть полезен для проверки факта инициализации Nav3D и/или выполнения определенных действий в момент инициализации.

Содержит:

  • Свойство bool Inited, которое показывает, был ли Nav3D уже проинициализирован.
  • Событие event Action OnNav3DInit. Оно срабатывает сразу после инициализации Nav3D. Подписавшись на него вы сможете выполнить какие-либо действия с Nav3D сразу после инициализации. При подписке на ивент ваш делегат сработает моментально, в случае если инициализация уже была произведена ранее.

В случае попытки использования публичных методов из классов пространства Nav3D.API до инициализации Nav3D будет выброшено исключение Nav3DManagerNotInitializedException. Любые действия с сущностями Nav3D можно производить только после его инициализации.

3. Nav3DInitializer

Для использования Nav3D в игровом режиме, надо его проинициализировать.

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

 

На сцене появится следующий игровой объект:

  • Init On Awake – Оставьте включенным, чтобы Nav3D выполнил инициализацию в игровом режиме при выполнении события Awake.
  • Dispose On Destroy – Оставьте включенным, чтобы Nav3D почистил все свои выделенные сущности при уничтожении сцены. Если отключите, то сущности Nav3D останутся и продолжат функционирование и на следующей сцене, которая будет загружена.
  • Min Bucket Size – Минимальный размер ячеек навигационного графа. Должен быть положительным ненулевым числом. Тут поясним чуть подробнее. По сути этот параметр позволяет настраивать уровень детализации навигационного графа. Чем меньше значение параметра - тем выше детализация графа.

Все игровые агенты, которые будут искать путь при помощи Nav3D имеют свой размер, определяемый радиусом, который вы им задаете при создании. (Об этом будет рассказано чуть позже). Минимальный размер ячейки должен быть равен максимальному из радиусов агентов, это будет гарантировать, что агент сможет пройти по любому пути на графе. Если на сцене будут находиться агенты, радиус которых превосходит минимальный размер ячейки графа поиска, то возможны ситуации, когда агенты будут задевать препятствия на сцене. В будущих версиях Nav3D будет реализован поиск пути с учетом размера агента.

*Если параметры Init On Awake и Dispose On Destroy будут отключены, то инициализация и освобождение ресурсов Nav3D будет лежать на вашей ответственности. Для инициализации вам надо будет вызвать метод Nav3DInitializer.Init(), для утилизации - Nav3DInitializer.Utilize(). Значение параметра Min Bucket Size также может быть установлено через код, для этого используйте свойство MinBucketSize.

4. Operations with obstacles

Вне зависимости от режима обработки препятствий процедура обработки состоит из нескольких этапов:

  1. Сбор информации о геометрии препятствия для каждого Nav3DObstacle.

    На данном этапе происходит получение списка всех геометрий, образующих препятствие, затем происходит их объединение (кластеризация).

    Положим, в transform иерархии препятствия есть несколько дочерних элементов, у каждого из которых есть MeshFilter с назначенным Mesh. Соответственно, в таком случае будет получено несколько списков треугольников, из которых состоят меши. Далее для каждого списка треугольников будет определен охватывающий объем в виде Bounds. Те списки, чьи Bounds пересекаются, будут объединены. Будем называть каждый такой отдельный список “объемом препятствия”. В результате получается один или несколько объемов препятствий. Это будет проделано для каждого обрабатываемого Nav3DObstacle на сцене.

  2. Кластеризация с уже обработанными препятствиями.

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

  3. Удаление из хранилища неактуальных объемов хранимых препятствий

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

  4. Далее происходит построение графов проходимости для новых объемов, затем эти объемы (вместе с графом внутри) добавляются в хранилище препятствий. navigation graphs are constructed for new volumes.

    These volumes are then added to the obstacle storage.

Удаление препятствия из хранилища состоит из следующих этапов:

  1. Из хранилища получается список всех объемов, содержащих треугольники удаляемого препятствия (в каждом объеме могут быть треугольники нескольких препятствий).
  2. Для каждого объема из полученного списка происходит удаление треугольников удаляемого препятствия.
  3. Далее рассматриваются только объемы, в которых после изъятия еще остались треугольники. Для этих объемов пересчитываются ограничивающие Bounds.
  4. Все объемы из первоначального списка удаляются из хранилища препятствий.
  5. Все оставленные объемы с пересчитанными Bounds кластеризуются и вновь добавляются в хранилище препятствий.

5. Dealing with Obstacles

Nav3D позволяет использовать в качестве препятствий игровые объекты на сцене с компонентом MeshFilter с назначенным Mesh, либо с компонентом Terrain.

*Для препятствий, использующих MeshFilter, убедитесь, что для mesh настройках импорта включена опция “Read/Write Enabled”.

5.1 Nav3DObstacle

Для управления препятствиями на сцене используется компонент Nav3DObstacle. Добавьте его на игровой объект, который вы хотите сделать препятствием на сцене.

 

Вы увидите инспектор компонента:

В нем есть следующие настройки:

  • Выбор режима обработки препятствия (обработка препятствия должна выполняться после инициализации Nav3D, чтобы оно учитывалось при поиске пути и, агенты могли его обойти во время движения):
    • Runtime - препятствие будет обработано прямо во время игрового режима после инициализации Nav3D. Это может быть полезно, если препятствие генерируется во время игрового режима.
    • Load from binary - препятствие будет загружено из заранее заготовленного бинарного файла. Во многих случаях обстановка игровой сцены известна заранее, еще до загрузки сцены и инициализации Nav3D. В таких случаях всегда имеет смысл запекать препятствия сцены в режиме редактора, а на старте сцены загружать их из бинарного файла. Загрузка из бинарного файла всегда гораздо быстрее, чем обработка препятствия непосредственно в игровом режиме (runtime).
  • Auto add child transforms - если включено, то для всех детей transform дерева будет совершен сбор информации о компонентах MeshFilter и Terrain. И все дети, имеющие данные компоненты, будут учтены при обработке препятствия                      *If you want any child element of an obstacle containing a MeshFilter or Terrain not to be taken into account when constructing the navigation graph, then attach the Nav3DObstacleIgnoreTag component to it.
  • Update automatically on transform change - если включено, то в случае изменения положения, поворота или масштаба transform компонента препятствия, будет выполняться его повторная обработка чтобы актуализировать граф поиска. (Препятствие будет сначала удалено из хранилища препятствий, затем будет повторно обработано и добавлено в него).Данная функция доступна для использования только для препятствий с выбранным Runtime режимом обработки.

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

  • Update min period - как часто должно обновляться препятствие (в секундах) при включенном параметре “Update automatically on transform change”

Для runtime-препятствий возможно их удаление/повторное добавление в хранилище прямо во время игры. Добавление происходит при срабатывании OnEnable(), удаление происходит при срабатывании OnDisable(). Соответственно, любое runtime-препятствие, созданное на сцене во время игры будет обработано и добавлено в хранилище. Если вы удалите или отключите игровой объект препятствия, то оно удалится из хранилища. Повторное включение препятствия вновь добавить его в хранилище.

5.2 Nav3DObstacleLoader

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

 

На сцене появится следующий игровой объект:

 

Для всех препятствий на сцене, которые вы хотите запечь, выберите режим обработки Load from binary

 

Далее в инспекторе компонента Nav3DObstacleLoader вы сможете увидеть список всех игровых объектов с компонентом Nav3DObstacle, подлежащих запеканию.

 

Нажмите кнопку “Bake and serialize obstacles” и выберите папку в проекте для сохранения бинарного файла с запеченными данными о препятствиях на сцене.

После успешного запекания инспектор компонента Nav3DObstacleLoader примет такой вид:

 

Инспектор успешно сериализованных Nav3DObstacle будет иметь такой вид:

 

Теперь в игровом режиме при инициализации Nav3D навигационные графы всех запеченных препятствиях на сцене будут загружены из бинарного файла.

5.3 Deeper dive into obstacles

  • Как уже было описано выше, для того чтобы препятствие было учтено при поиске пути, необходимо навесить на его игровой объект компонент Nav3DObstacle
  • В Nav3D существует хранилище препятствий, которое используется при поиске пути. В нем хранятся навигационные графы препятствий на сцене.
  • Все операции с препятствиями (добавление или удаление) ставятся в очередь исполнения и производятся последовательно в отдельном потоке.
  • Под обработкой препятствия подразумевается построение графа проходимости (он же навигационный граф) для каждого препятствия, либо для группы препятствий, а затем добавление этого графа в хранилище препятствий.

Графы проходимости используются при поиске пути на сцене.

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

5.3.1 Operations with obstacles

Вне зависимости от режима обработки препятствий процедура обработки состоит из нескольких этапов:

  1. Сбор информации о геометрии препятствия для каждого Nav3DObstacle.

    На данном этапе происходит получение списка всех геометрий, образующих препятствие, затем происходит их объединение (кластеризация).

    Положим, в transform иерархии препятствия есть несколько дочерних элементов, у каждого из которых есть MeshFilter с назначенным Mesh. Соответственно, в таком случае будет получено несколько списков треугольников, из которых состоят меши. Далее для каждого списка треугольников будет определен охватывающий объем в виде Bounds. Те списки, чьи Bounds пересекаются, будут объединены. Будем называть каждый такой отдельный список “объемом препятствия”. В результате получается один или несколько объемов препятствий. Это будет проделано для каждого обрабатываемого Nav3DObstacle на сцене.

  2. Кластеризация с уже обработанными препятствиями.

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

  3. Удаление из хранилища неактуальных объемов хранимых препятствий

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

  4. Далее происходит построение графов проходимости для новых объемов, затем эти объемы (вместе с графом внутри) добавляются в хранилище препятствий. navigation graphs are constructed for new volumes.

    These volumes are then added to the obstacle storage.

Удаление препятствия из хранилища состоит из следующих этапов:

  1. Из хранилища получается список всех объемов, содержащих треугольники удаляемого препятствия (в каждом объеме могут быть треугольники нескольких препятствий).
  2. Для каждого объема из полученного списка происходит удаление треугольников удаляемого препятствия.
  3. Далее рассматриваются только объемы, в которых после изъятия еще остались треугольники. Для этих объемов пересчитываются ограничивающие Bounds.
  4. Все объемы из первоначального списка удаляются из хранилища препятствий.
  5. Все оставленные объемы с пересчитанными Bounds кластеризуются и вновь добавляются в хранилище препятствий.

5.3.2 Obstacle combinations

Будем называть препятствия с выбранным режимом обработки “Runtime” как runtime-препятствия, а с режимом “Load from binary” - static-препятствия.

Если вы создаете runtime-препятствие во время игрового режима, то оно будет обработано и добавлено в хранилище (при срабатывании OnEnable()). Если вы отключите на сцене уже добавленное runtime-препятствие, то оно будет удалено из хранилища (при срабатывании OnDisable()).

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

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

Так что мы решили ввести несколько ограничений:

  • В момент инициализации сначала происходит загрузка и добавление всех статичных препятствий, затем обработка и добавление runtime-препятствий (если таковые имеются на сцене).
  • При обработке и добавлении runtime-препятствия его объем не должен пересекать объемы static препятствий, в противном случае будет выброшено исключение IntersectStaticObstacleException.
  • Как уже было сказано выше, добавление нескольких runtime-препятствий может привести к тому, что для них будет образован объединенный объем, больший чем каждый из их объемов в отдельности. Ваша задача позаботиться о том, чтобы возможный объединенный объем не пересекал объемы static-препятствий. Для проверки такого потенциального столкновения есть метод Nav3DManager.BoundsCrossStaticObstacles(Bounds _Bounds).

Для избегания таких ситуаций предлагаем вам вообще отказаться от совместного использования static и runtime препятствий на одной сцене.

Если вы все-таки решили использовать оба вида, то рекомендуем создавать runtime-препятствия вдалеке от static препятствий, чтобы минимизировать вероятность пересечения объемов.

6. Debug drawing

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

1. Визуализация радиуса действия агента.

2. Визуализация векторов скорости агента.

3. Визуализация пути, по которому следует агент.

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

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

6. Все.

7. Nav3DManager

Nav3DManager - вспомогательный static класс. Может быть полезен для проверки факта инициализации Nav3D и/или выполнения определенных действий в момент инициализации.

Содержит:

  • Свойство bool Inited, которое показывает, был ли Nav3D уже проинициализирован.
  • Событие event Action OnNav3DInit. Оно срабатывает сразу после инициализации Nav3D. Подписавшись на него вы сможете выполнить какие-либо действия с Nav3D сразу после инициализации. При подписке на ивент ваш делегат сработает моментально, в случае если инициализация уже была произведена ранее.

В случае попытки использования публичных методов из классов пространства Nav3D.API до инициализации Nav3D будет выброшено исключение Nav3DManagerNotInitializedException. Любые действия с сущностями Nav3D можно производить только после его инициализации.

8. Obstacle combinations

Будем называть препятствия с выбранным режимом обработки “Runtime” как runtime-препятствия, а с режимом “Load from binary” - static-препятствия.

Если вы создаете runtime-препятствие во время игрового режима, то оно будет обработано и добавлено в хранилище (при срабатывании OnEnable()). Если вы отключите на сцене уже добавленное runtime-препятствие, то оно будет удалено из хранилища (при срабатывании OnDisable()).

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

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

Так что мы решили ввести несколько ограничений:

  • В момент инициализации сначала происходит загрузка и добавление всех статичных препятствий, затем обработка и добавление runtime-препятствий (если таковые имеются на сцене).
  • При обработке и добавлении runtime-препятствия его объем не должен пересекать объемы static препятствий, в противном случае будет выброшено исключение IntersectStaticObstacleException.
  • Как уже было сказано выше, добавление нескольких runtime-препятствий может привести к тому, что для них будет образован объединенный объем, больший чем каждый из их объемов в отдельности. Ваша задача позаботиться о том, чтобы возможный объединенный объем не пересекал объемы static-препятствий. Для проверки такого потенциального столкновения есть метод Nav3DManager.BoundsCrossStaticObstacles(Bounds _Bounds).

Для избегания таких ситуаций предлагаем вам вообще отказаться от совместного использования static и runtime препятствий на одной сцене.

Если вы все-таки решили использовать оба вида, то рекомендуем создавать runtime-препятствия вдалеке от static препятствий, чтобы минимизировать вероятность пересечения объемов.

9. Agent log

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

10. Nav3DObstacle

Для управления препятствиями на сцене используется компонент Nav3DObstacle. Добавьте его на игровой объект, который вы хотите сделать препятствием на сцене.

 

Вы увидите инспектор компонента:

В нем есть следующие настройки:

  • Выбор режима обработки препятствия (обработка препятствия должна выполняться после инициализации Nav3D, чтобы оно учитывалось при поиске пути и, агенты могли его обойти во время движения):
    • Runtime - препятствие будет обработано прямо во время игрового режима после инициализации Nav3D. Это может быть полезно, если препятствие генерируется во время игрового режима.
    • Load from binary - препятствие будет загружено из заранее заготовленного бинарного файла. Во многих случаях обстановка игровой сцены известна заранее, еще до загрузки сцены и инициализации Nav3D. В таких случаях всегда имеет смысл запекать препятствия сцены в режиме редактора, а на старте сцены загружать их из бинарного файла. Загрузка из бинарного файла всегда гораздо быстрее, чем обработка препятствия непосредственно в игровом режиме (runtime).
  • Auto add child transforms - если включено, то для всех детей transform дерева будет совершен сбор информации о компонентах MeshFilter и Terrain. И все дети, имеющие данные компоненты, будут учтены при обработке препятствия                      *If you want any child element of an obstacle containing a MeshFilter or Terrain not to be taken into account when constructing the navigation graph, then attach the Nav3DObstacleIgnoreTag component to it.
  • Update automatically on transform change - если включено, то в случае изменения положения, поворота или масштаба transform компонента препятствия, будет выполняться его повторная обработка чтобы актуализировать граф поиска. (Препятствие будет сначала удалено из хранилища препятствий, затем будет повторно обработано и добавлено в него).Данная функция доступна для использования только для препятствий с выбранным Runtime режимом обработки.

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

  • Update min period - как часто должно обновляться препятствие (в секундах) при включенном параметре “Update automatically on transform change”

Для runtime-препятствий возможно их удаление/повторное добавление в хранилище прямо во время игры. Добавление происходит при срабатывании OnEnable(), удаление происходит при срабатывании OnDisable(). Соответственно, любое runtime-препятствие, созданное на сцене во время игры будет обработано и добавлено в хранилище. Если вы удалите или отключите игровой объект препятствия, то оно удалится из хранилища. Повторное включение препятствия вновь добавить его в хранилище.

11. Randomly generated parameters

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

Существует два типа распределения: непрерывное равномерное и гауссово. Первый делает то же самое, что и UnityEngine.Random.Range(a, b). Тип распределения по Гауссу можно описать следующим образом: большинство значений будет выбрано примерно из середины диапазона, но будет несколько значений, которые находятся ближе к концам диапазона.

Ниже приведен пример установки радиуса для диапазона [0.1, 0.3] в соответствии с распределением Гаусса.

  • Все классы ассета, необходимые вам для работы, содержатся в пространстве имен Nav3D.API.

12. Particular resolution regions

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

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

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

Для этого мы реализовали объект Nav3DParticularResolutionRegion, который позволяет задавать регион в пространстве, в котором при построении будет использован специфический минимальный размер ячеек графа поиска.

12.1 Nav3DParticularResolutionRegion

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

  • Все классы ассета, необходимые вам для работы, содержатся в пространстве имен Nav3D.API.

Задайте появившемуся игровому объекту нужное положение на сцене. Далее в инспекторе компонента Nav3DParticularResolutionRegion настройте размеры области, а также укажите нужный минимальный размер ячеек графа поиска . Для визуализации региона включите отображение Gizmos в окне сцены.

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

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

12.2 Nav3DParticularResolutionRegion : Some points

  • Использование регионов разрешения подразумевается для решения задач связанных с проходимостью графа в тесных областях пространства, таких как например узкие тоннели, маленькие отверстия, сквозь которые должна быть обеспечена возможность поиска пути. Но также в таких регионах можно задавать размер минимальной ячейки графа поиска больший, чем размер установленный на всей сцене. Это может быть полезно, когда вы наоборот хотите избежать избыточной детализации графа поиска для некоторых препятствий. Избегание избыточно высокого разрешения графа поиска благоприятно скажется на общей производительности при поиске пути.
  • Октодерево является иерархической структурой, в которой размеры всех ячеек всегда кратны размеру друг друга и от уровня к уровню размер меняется с множителем два (каждый следующий уровень содержит ячейки в два раза меньше чем в предыдущем). Так что вы можете указать лишь желаемый минимальный размер ячейки. На деле реальный минимальный размер ячейки будет вычислен относительно размера указанного при инициализации Nav3D. Если например при инициализации минимальный размер был указан как 0.4, а вы указываете желаемый минимальный размер как 0.3, то фактически будет выбран размер 0.2, поскольку он должен быть уровнем ниже и получен из 0.4 делением на 2. Если же вы выберете желаемый размер как 0.7, то фактический размер будет равен 0.8, поскольку он должен быть уровнем выше и получен умножением на 2.
  • В случае если препятствие находится внутри пересечения нескольких регионов с разным минимальным размером ячеек, при обработке в качестве минимальной ячейки будет выбрано наименьшее значение из значений регионов.
  • Рекомендуем использовать регионы в качестве статических объектов, находящихся на сцене в момент обработки всех препятствий. Операция добавления/удаления региона во время игры повлечет повторную обработку тех runtime-препятствий, которые пересекаются регионом. Static-препятствия никак не отреагируют на удаление/добавление региона. Инициализация и деинициализация региона происходит по событиям OnEnable() и соответственно DoDisable() в Nav3DParticularResolutionRegion. Так что для инициализации региона можно создать его префаб на сцене, для деинициализации - сделать неактивным его GameObject, либо уничтожить его.

13. Creating and configuring an agent description from code

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

Для этого вам нужно создать переменную типа Nav3DAgentDescription, затем применить ее к агенту. Правильный способ создания описание - обращение к свойству Nav3DAgentDescription.DefaultDescription .

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

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

				
					void ConfigureAgentDescription()
{
   Nav3DAgent myAgent = GetComponent<Nav3DAgent>();

   //create Nav3DAgentDescription instance with default parameters
   Nav3DAgentDescription myDescription = Nav3DAgentDescription.DefaultDescription;

   //set the parameters you want
   myDescription.Radius = 1.2f;
   myDescription.MotionNavigationType = MotionNavigationType.LOCAL;
  
   //apply the description to an agent
   myAgent.SetDescription(myDescription.GetDescriptionVariant());
}
				
			

14. Path object

Класс Path предназначен для работы с траекториями в пространстве.

Как правило, объекты этого типа используются агентами(Nav3DAgent)при выполнении поиска пути. Но вы также можете использовать Path , чтобы найти путь для своих собственных целей.

Разумеется, работа с объектами Path возможна только после инициализации Nav3D. Подробнее читайте в разделе “Инициализация Nav3D”.

14.1 Nav3DPathfindingManager

Данный класс содержит инструменты для работы с поиском путей на сцене.

Для достижения необходимого уровня производительности при использовании Nav3D могут быть полезны следующие свойства:

  • public static int CurrentPathfindingTasksCount – показывает сколько параллельных задач поиска пути активны на данный момент;
  • public static int MaxPathfindingTasks – позволяет устанавливать ограничение на максимальное количество задач поиска пути, работающих единовременно. По умолчанию данное свойство установлено на значение Environment.ProcessorCount - 1

Также имеется метод public static Path PrefetchPath(Vector3 _PointA, Vector3 _PointB) для создания экземпляра Path. О его применении сказано ниже.

14.2 Pathfinding

Для поиска пути между точками A и B необходимо:

Получить объект типа Path , вызвав метод

				
					Nav3DPathfindingManager.PrefetchPath(Vector3 _PointA, Vector3 _PointB)
				
			

Данный метод лишь создаст экземпляр Path и зарегистрирует его в Nav3D, но поиск пути осуществлен не будет.

Для созданного объекта Path вызовите метод

				
					UpdatePath(
                Vector3? _Start = null,
            	Vector3? _Goal = null,
            	int? _Timeout = null,
            	bool? _Smooth = null,
            	Action _OnSuccess = null,
            	Action<PathfindingError> _OnFail = null
)
				
			

Значения перегруженных параметров:

  • Vector3? _Start точка начала пути. Параметр необходимо передавать если вы хотите поменять точку начала пути.
  • Vector3? _Goal конечная точка пути. Параметр необходимо передавать если вы хотите поменять конечную точку пути.
  • int? _Timeout максимальное время поиска пути. В случае, если поиск пути длится дольше, произойдет выполнение колбека _OnFail, если он не равен null.
  • bool? _Smooth = null необходимо ли сглаживать найденный путь.
  • Action _OnSuccess null - колбек, который вызовется после успешного нахождения пути
  • Action _OnFail колбек, который вызовется в случае, если путь не удалось найти по какой-либо причине.

14.3 PathfindingError

Класс, экземпляр которого возвращается в колбек _OnFail метода Path.UpdatePath() .

Содержит два свойства:

  • public PathfindingResultCode Reason причина неудачи поиска пути
    Возможные значения свойства:
ValueОписание
SUCCEEDEDПоиск пути завершился успешно.
PATH_DOES_NOT_EXISTМежду точками не существует пути. Это возможно если хотя бы одна из точек окружена препятствиями со всех сторон.
TIMEOUTПоиск пути занял больше времени чем разрешено и был прерван.
CANCELEDПоиск пути был завершен по указанию пользователя, или по внутренней логике Nav3D.
START_POINT_INSIDE_OBSTACLEПоиск пути не удался, потому что начальная точка пути находится внутри препятствия
GOAL_POINT_INSIDE_OBSTACLEПоиск пути не удался, потому что конечная точка пути находится внутри препятствия.
UNKNOWNВнутренняя ошибка.
  • public string Msg сообщение с более подробной информацией об ошибке.

14.4 Path : Properties

  • public Vector3[] Trajectory { get; } - траектория найденного пути. Используйте это свойство для получения последнего найденного пути. Значение является верным, если метод UpdatePath был вызван и завершился успешно и никакие прочие свойства объекта Path не менялись после этого. Используйте свойство IsValid для проверки того, что значение верно.
  • public Vector3[] TrajectoryOriginal {get;} исходная найденная траектория.
  • public Vector3[] TrajectoryOptimized {get;} оптимизированная найденная траектория.
  • public Vector3[] TrajectorySmoothed { get; } сглаженная траектория найденного пути. Значение будет отличаться от TrajectoryOptimized, если свойство Smooth имеет значение true.
  • public Bounds Bounds { get; } объем пространства, который занят найденным путем.
  • public Vector3 Start { get; set; } точка начала пути.
  • public Vector3 Goal { get; set; } конечная точка пути.
  • public int SmoothRatio { get; set; } - количество проходов сглаживания по отношению к минимальному размеру ячейки графа поиска, по умолчанию установлено как 3. Не рекомендуем увеличивать это значение без необходимости, так как это повлечет рост времени поиска пути, в случае если сглаживание включено (свойство Smooth == true).
  • public int Timeout { get; set; } - максимальное время поиска пути. В случае, если поиск пути длится дольше, произойдет выполнение колбека Action<PathfindingError> _OnFail , если он был передан в UpdatePath().
  • public bool Smooth { get; set; } нужно ли производить сглаживание пути.
  • public bool IsValid { get; } - является ли текущее значение Trajectory не null и верно ли оно для текущих значений остальных свойств. Например если вы вызвали UpdatePath(), а после его успешного завершения изменили значение свойства Start, то путь уже не будет являться актуальным и IsValid будет равно false.
  • public bool IsPathUpdating { get; } находится ли экземпляр Path в процессе поиска пути.

14.5 Completing dealing with the Path Object

После того как вы завершили работу с объектом Path, необходимо вызвать метод Path.Dispose() method. This is necessary to remove the path from the obstacle storage. Otherwise, when adding / updating / removing any obstacle, the path will be checked for the need to update, which will take up computing resources.

14.6 Usage example

Ниже приведен пример использования объекта Path для нахождения пути между А и В

				
					using Nav3D.API;
using System;
using UnityEngine;

class YourGameScenario : MonoBehaviour
{
    private void Start()
    {
        Nav3DManager.InitNav3D(1f);

        Vector3 a = new Vector3(-10, 10, 10);
        Vector3 b = new Vector3(-10, 10, 10);

        FindPath(a, b, PrintPath);
    }

    //your method for pathfinding from A to B
    void FindPath(Vector3 _A, Vector3 _B, Action<Vector3[]> _OnPathFound)
    {
        //create Path instance
        Path path = Nav3DPathfindingManager.PrefetchPath(_A, _B);

        //perform pathfinding from _A to _B
        path.UpdatePath(_OnSuccess: () =>
        {
            //notify that path was found
            UnityEngine.Debug.Log("Path successfully found!");

            //invoke callback
            _OnPathFound(path.Trajectory);

            //finish work with Path instance
            path.Dispose();
        });
    }

    void PrintPath(Vector3[] _Path)
    {
        for (int i = 0; i < _Path.Length - 1; i++)
            UnityEngine.Debug.LogError($"Path point {i}: {_Path[i]}");
    }
}
				
			

15. Nav3DObstacleLoader

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

 

На сцене появится следующий игровой объект:

 

Для всех препятствий на сцене, которые вы хотите запечь, выберите режим обработки Load from binary

 

Далее в инспекторе компонента Nav3DObstacleLoader вы сможете увидеть список всех игровых объектов с компонентом Nav3DObstacle, подлежащих запеканию.

 

Нажмите кнопку “Bake and serialize obstacles” и выберите папку в проекте для сохранения бинарного файла с запеченными данными о препятствиях на сцене.

После успешного запекания инспектор компонента Nav3DObstacleLoader примет такой вид:

 

Инспектор успешно сериализованных Nav3DObstacle будет иметь такой вид:

 

Теперь в игровом режиме при инициализации Nav3D навигационные графы всех запеченных препятствиях на сцене будут загружены из бинарного файла.

16. Agents

Nav3DAgent то основной класс, осуществляющий движение игровых юнитов в пространстве игровой сцены.

Nav3DAgent Использование публичных методов возможно только после инициализации Nav3D. Подробнее читайте в разделе Nav3DInitializer.

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

Для использования Nav3DAgentнеобходимо настроить его поведение. Это возможно сделать с помощью использования класса Nav3DAgentDescription .

16.1 Nav3DAgentDescription

				
					Nav3DAgentDescription
				
			

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

После создания описания в инспекторе вы увидите следующий список групп параметров:

Кнопка Set default parameters ускорит настройку всех параметров, выставив их значения по умолчанию.

Приведем пояснение назначения параметров и их допустимые величины.

Behavior

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Behavior Type
Behavior Type
enum BehaviorType
DEFAULT, YIELDING, INDIFFERENT

Описывает тип поведения агента.

  • Агент может вести себя стандартным образом (DEFAULT), т.е.искать путь, за тем следовать по нему, или выполнять любые другие указания пользователя. Поведение агента будет полностью определяться его описанием.
  • Агент может иметь уступающее поведение (YIELDING). Он не сможет выполнять команды пользователя и не будет иметь собственных задач.
    (При попытке вызова методов движения к цели будет выброшено исключение.) Его единственной заботой будет уступать дорогу агентам поблизости. С ними он будет взаимодействовать с помощью механизма Local avoidance, в соответствии со своими параметрами скорости и радиуса.
  • Также агент может быть безразличным (INDIFFERENT).
    Агент не будет делать ничего, просто будет находиться на месте. Другие агенты смогут обходить его при выполнении локального уклонения. Попытка вызова методов следования к цели с таким типом поведения повлечет выброс исключения, по аналогии с YIELDING.
Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Motion Navigation Type
MotionNavigationType
enum MotionNavigationType
GLOBAL, GLOBAL_AND_LOCAL, LOCAL

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

  • Используя глобальный путь (GLOBAL). В таком случае агент выполнит поиск пути и будет двигаться по нему, невзирая на внешние условия (другие агенты, находящиеся слишком близко края препятствий).
  • С использованием только локального уклонения (LOCAL). Агент не будет использовать поиск пути для движения, а будет двигаться к цели по прямой, попутно уклоняясь от других агентов и препятствий, когда они встречаются на курсе движения. Разумеется не всякое препятствие может быть преодолено таким образом.
  • Наконец, агент может осуществлять движение к цели используя поиск пути и локальное уклонение одновременно (GLOBAL_AND_LOCAL). Агент будет двигаться по найденному пути и уклоняться от подошедших близко других агентов, а также находящихся слишком близко препятствий.

Radius

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Radius
Radius
float
>0

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

Speed

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Speed
Speed
float
>0

Скорость движения агента. Можете менять ее в любой момент времени, если потребуется.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Max Speed
Max Speed
float
>Speed

Максимальная скорость агента. Этот параметр нужен для выполнения локального уклонения. В некоторых ситуациях агенту может потребоваться ускориться, чтобы избежать столкновения. Если значение будет слишком велико, то порой агент может двигаться рывками при выполнении локального уклонения. Так что советуем выставлять максимальную скорость чуть больше чем Speed(больше менее чем в полтора раза).

Для вашего удобства в инспекторе имеется два способа установления этого значения. С помощью конкретного значения. Либо с помощью множителя для Speed. Советуем использовать множитель, для того чтобы максимальная скорость зависела от Speed агента. Тогда MaxSpeed будет меняться вслед за изменением Speed

Local avoidance

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
ORCA Tau
ORCATau
float
>0

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

Лучше понять его смысл вам поможет оригинальная статья по ORCA Reciprocal n-body Collision Avoidance. Jur van den Berg, Stephen J. Guy, Ming Lin, and Dinesh Manocha

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Agents considered number limit
Agents considered number limit
bool
true, false

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

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Agents number
ConsideredAgentsNumberLimit
int
>=1

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

Поиск пути

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Pathfinding timeout (ms)
PathfindingTimeout
int
>0

Максимальное время, отведенное на выполнение поиска пути. При превышении этого времени поиск(pathfinding) будет отменен, и будет вызван колбек _OnPathfindingFail.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Smooth the path
SmoothPath
bool
true, false

Нужно ли сглаживать найденный путь.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Samples per min bucket volume
SmoothRatio
int
>0

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

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Auto-update path on stagnant behavior
AutoUpdatePath
bool
true, false

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

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

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Auto-update cooldown (ms)
PathAutoUpdateCooldown
int
>=1

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

Motion

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Target reach distance
TargetReachDistance
float
>=0

Расстояние достижения цели. Когда вы отдаете агенту приказание достичь какой-либо цели, цель будет считаться достигнутой, когда pivot агента совпадет с координатой цели (расстояние между ними будет равным 0). Это может быть неудобно, если целью является объект с мешем, допустим планета в космосе. В таком случае выставьте этот параметр равным сумме радиуса видимой части агента и радиуса планеты. Тогда агент остановится, достигнув поверхности планеты.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Max rotation in degrees per fixed update tick
MaxAgentDegreesRotationPerTick
float
>=0

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

Velocity blending

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

  1. Вектор скорости следования по пути.
  2. Вектор скорости уклонения от агентов.
  3. Вектор скорости уклонения от препятствий.

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

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

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

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

Все веса задаются типом float и должны иметь значение более 0

Соответствующие названия свойств в Nav3DAgentDescription:

  • PathVelocityWeight
  • PathVelocityWeight1
  • PathVelocityWeight2
  • AgentsAvoidanceVelocityWeight
  • AgentsAvoidanceVelocityWeight1
  • AgentsAvoidanceVelocityWeight2
  • ObstacleAvoidanceVelocityWeight
  • ObstacleAvoidanceVelocityWeight1
  • ObstacleAvoidanceVelocityWeight2

Debug

Use Log UseLog bool Whether it is necessary to log agent work processes. true, false
Log records count
LogSize
int
Agent log size.
>0

16.1.1 Randomly generated parameters

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

Существует два типа распределения: непрерывное равномерное и гауссово. Первый делает то же самое, что и UnityEngine.Random.Range(a, b). Тип распределения по Гауссу можно описать следующим образом: большинство значений будет выбрано примерно из середины диапазона, но будет несколько значений, которые находятся ближе к концам диапазона.

Ниже приведен пример установки радиуса для диапазона [0.1, 0.3] в соответствии с распределением Гаусса.

  • Все классы ассета, необходимые вам для работы, содержатся в пространстве имен Nav3D.API.

16.1.2 Creating and configuring an agent description from code

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

Для этого вам нужно создать переменную типа Nav3DAgentDescription, затем применить ее к агенту. Правильный способ создания описание - обращение к свойству Nav3DAgentDescription.DefaultDescription .

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

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

				
					void ConfigureAgentDescription()
{
   Nav3DAgent myAgent = GetComponent<Nav3DAgent>();

   //create Nav3DAgentDescription instance with default parameters
   Nav3DAgentDescription myDescription = Nav3DAgentDescription.DefaultDescription;

   //set the parameters you want
   myDescription.Radius = 1.2f;
   myDescription.MotionNavigationType = MotionNavigationType.LOCAL;
  
   //apply the description to an agent
   myAgent.SetDescription(myDescription.GetDescriptionVariant());
}
				
			

16.2 Nav3DAgent

Как упоминалось выше, вы можете использовать скрипт Nav3DAgent в качестве компонента для вашего игрового объекта и получить к нему доступ через GetComponent() или вы можете создать класс-наследник, который лично мы считаем гораздо более удобным.

Для работы с Nav3DAgent были реализованы следующие публичые методы:
  • Применяет описание к вашему агенту.
				
					public void SetDescription(Nav3DAgentDescription _Description)
				
			
  • Команда двигаться к точке. Любая действующая команда будет прервана, агент начнет движение к точке, в соответствии с параметрами его описания.
				
					public void MoveTo(Vector3 _Point, Action _OnReach = null, Action<PathfindingError> _OnPathfindingFail = null)
				
			
  • Добавить точку следования в очередь. Таким образом может быть добавлено сколько угодно точек следования. Если агент выполняет команду FollowTarget, то она прервется и он начинает выполнять команду следование к точке.
				
					public void MoveToEnqueue(Vector3 _Point, Action _OnReach = null, Action<PathfindingError> _OnPathfindingFail = null)
				
			
  • Команда преследовать движущийся Transform.

				
					public void FollowTarget(
            Transform _Target,
            float _OffsetToleranceUpdate,
            float _DistToReach = 0,
            Action _OnReach = null,
            Action _OnCancel = null,
            Action<PathfindingError> _OnPathfindingFail = null
        )
				
			
  • Transform _Target – Transform, который необходимо преследовать.
  • float _OffsetToleranceUpdate – величина минимального смещения цели, которое приведет к обновлению пути к цели. Если ваш агент имеет параметр Motion Navigation Type описания установленным в значение GLOBAL, или GLOBAL_AND_LOCAL, то каждый раз при смещении преследуемого Transform более чем на _OffsetToleranceUpdate произойдет обновление пути.
  • If your agent has the Motion Navigation Type of the description set to GLOBAL, or GLOBAL_AND_LOCAL, then each time the pursued Transform is offset by more than _OffsetToleranceUpdate , it will cause the path to be recalculated.
  • float _DistToReach – расстояние до цели, приблизившись на которое цель будет считаться достигнутой. Хотим заметить, что в случае когда агенту приходится искать глобальный путь, цель может никогда не быть достигнутой, поскольку достигнув конца пути может оказаться, что цель за прошедшее время сдвинулась. Таким образом условие совпадения координат преследуемой цели и агента может быть труднодостижимым.
  • Action _OnReach – Делегат, который будет выполнен, когда цель будет успешно достигнута.
  • Action _OnCancel – делегат, выполняемый в случае, если ссылка на преследуемый Transform стала null, либо missing.
  • Прерывает выполнение текущей команды.
				
					public void Stop()
				
			
  • Позволяет получить список агентов внутри радиуса _Radius от агента. Вызов этого метода по событию Update, может негативно сказаться на производительности, поэтому рекомендуется не вызывать его слишком часто (например, нескольких раз в секунду должно быть достаточно для любых игровых сценариев).
				
					public List<Nav3DAgent> GetAgentsInRadius(float _Radius, Predicate<Nav3DAgent> _Predicate = null)
				
			
  • Визуализирует агента с помощью Gizmos. Может быть полезно для отладки в Editor
				
					public void Visualize(bool _DrawRadius = true, bool _DrawPath = true, bool _DrawVelocities = true)
				
			

Вы можете найти примеры использования Nav 3D Agent в демонстрационных сценах, расположенных в папке Demo ресурса.

16.3 Nav3DAgent : Debug

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

16.3.1 Debug drawing

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

1. Визуализация радиуса действия агента.

2. Визуализация векторов скорости агента.

3. Визуализация пути, по которому следует агент.

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

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

6. Все.

16.3.2 Agent log

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

17. Deeper dive into obstacles

  • Как уже было описано выше, для того чтобы препятствие было учтено при поиске пути, необходимо навесить на его игровой объект компонент Nav3DObstacle
  • В Nav3D существует хранилище препятствий, которое используется при поиске пути. В нем хранятся навигационные графы препятствий на сцене.
  • Все операции с препятствиями (добавление или удаление) ставятся в очередь исполнения и производятся последовательно в отдельном потоке.
  • Под обработкой препятствия подразумевается построение графа проходимости (он же навигационный граф) для каждого препятствия, либо для группы препятствий, а затем добавление этого графа в хранилище препятствий.

Графы проходимости используются при поиске пути на сцене.

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

17.1 Operations with obstacles

Вне зависимости от режима обработки препятствий процедура обработки состоит из нескольких этапов:

  1. Сбор информации о геометрии препятствия для каждого Nav3DObstacle.

    На данном этапе происходит получение списка всех геометрий, образующих препятствие, затем происходит их объединение (кластеризация).

    Положим, в transform иерархии препятствия есть несколько дочерних элементов, у каждого из которых есть MeshFilter с назначенным Mesh. Соответственно, в таком случае будет получено несколько списков треугольников, из которых состоят меши. Далее для каждого списка треугольников будет определен охватывающий объем в виде Bounds. Те списки, чьи Bounds пересекаются, будут объединены. Будем называть каждый такой отдельный список “объемом препятствия”. В результате получается один или несколько объемов препятствий. Это будет проделано для каждого обрабатываемого Nav3DObstacle на сцене.

  2. Кластеризация с уже обработанными препятствиями.

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

  3. Удаление из хранилища неактуальных объемов хранимых препятствий

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

  4. Далее происходит построение графов проходимости для новых объемов, затем эти объемы (вместе с графом внутри) добавляются в хранилище препятствий. navigation graphs are constructed for new volumes.

    These volumes are then added to the obstacle storage.

Удаление препятствия из хранилища состоит из следующих этапов:

  1. Из хранилища получается список всех объемов, содержащих треугольники удаляемого препятствия (в каждом объеме могут быть треугольники нескольких препятствий).
  2. Для каждого объема из полученного списка происходит удаление треугольников удаляемого препятствия.
  3. Далее рассматриваются только объемы, в которых после изъятия еще остались треугольники. Для этих объемов пересчитываются ограничивающие Bounds.
  4. Все объемы из первоначального списка удаляются из хранилища препятствий.
  5. Все оставленные объемы с пересчитанными Bounds кластеризуются и вновь добавляются в хранилище препятствий.

17.2 Obstacle combinations

Будем называть препятствия с выбранным режимом обработки “Runtime” как runtime-препятствия, а с режимом “Load from binary” - static-препятствия.

Если вы создаете runtime-препятствие во время игрового режима, то оно будет обработано и добавлено в хранилище (при срабатывании OnEnable()). Если вы отключите на сцене уже добавленное runtime-препятствие, то оно будет удалено из хранилища (при срабатывании OnDisable()).

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

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

Так что мы решили ввести несколько ограничений:

  • В момент инициализации сначала происходит загрузка и добавление всех статичных препятствий, затем обработка и добавление runtime-препятствий (если таковые имеются на сцене).
  • При обработке и добавлении runtime-препятствия его объем не должен пересекать объемы static препятствий, в противном случае будет выброшено исключение IntersectStaticObstacleException.
  • Как уже было сказано выше, добавление нескольких runtime-препятствий может привести к тому, что для них будет образован объединенный объем, больший чем каждый из их объемов в отдельности. Ваша задача позаботиться о том, чтобы возможный объединенный объем не пересекал объемы static-препятствий. Для проверки такого потенциального столкновения есть метод Nav3DManager.BoundsCrossStaticObstacles(Bounds _Bounds).

Для избегания таких ситуаций предлагаем вам вообще отказаться от совместного использования static и runtime препятствий на одной сцене.

Если вы все-таки решили использовать оба вида, то рекомендуем создавать runtime-препятствия вдалеке от static препятствий, чтобы минимизировать вероятность пересечения объемов.

18. Nav3DParticularResolutionRegion

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

  • Все классы ассета, необходимые вам для работы, содержатся в пространстве имен Nav3D.API.

Задайте появившемуся игровому объекту нужное положение на сцене. Далее в инспекторе компонента Nav3DParticularResolutionRegion настройте размеры области, а также укажите нужный минимальный размер ячеек графа поиска . Для визуализации региона включите отображение Gizmos в окне сцены.

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

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

19. Nav3DParticularResolutionRegion : Some points

  • Использование регионов разрешения подразумевается для решения задач связанных с проходимостью графа в тесных областях пространства, таких как например узкие тоннели, маленькие отверстия, сквозь которые должна быть обеспечена возможность поиска пути. Но также в таких регионах можно задавать размер минимальной ячейки графа поиска больший, чем размер установленный на всей сцене. Это может быть полезно, когда вы наоборот хотите избежать избыточной детализации графа поиска для некоторых препятствий. Избегание избыточно высокого разрешения графа поиска благоприятно скажется на общей производительности при поиске пути.
  • Октодерево является иерархической структурой, в которой размеры всех ячеек всегда кратны размеру друг друга и от уровня к уровню размер меняется с множителем два (каждый следующий уровень содержит ячейки в два раза меньше чем в предыдущем). Так что вы можете указать лишь желаемый минимальный размер ячейки. На деле реальный минимальный размер ячейки будет вычислен относительно размера указанного при инициализации Nav3D. Если например при инициализации минимальный размер был указан как 0.4, а вы указываете желаемый минимальный размер как 0.3, то фактически будет выбран размер 0.2, поскольку он должен быть уровнем ниже и получен из 0.4 делением на 2. Если же вы выберете желаемый размер как 0.7, то фактический размер будет равен 0.8, поскольку он должен быть уровнем выше и получен умножением на 2.
  • В случае если препятствие находится внутри пересечения нескольких регионов с разным минимальным размером ячеек, при обработке в качестве минимальной ячейки будет выбрано наименьшее значение из значений регионов.
  • Рекомендуем использовать регионы в качестве статических объектов, находящихся на сцене в момент обработки всех препятствий. Операция добавления/удаления региона во время игры повлечет повторную обработку тех runtime-препятствий, которые пересекаются регионом. Static-препятствия никак не отреагируют на удаление/добавление региона. Инициализация и деинициализация региона происходит по событиям OnEnable() и соответственно DoDisable() в Nav3DParticularResolutionRegion. Так что для инициализации региона можно создать его префаб на сцене, для деинициализации - сделать неактивным его GameObject, либо уничтожить его.

20. Nav3DPathfindingManager

Данный класс содержит инструменты для работы с поиском путей на сцене.

Для достижения необходимого уровня производительности при использовании Nav3D могут быть полезны следующие свойства:

  • public static int CurrentPathfindingTasksCount – показывает сколько параллельных задач поиска пути активны на данный момент;
  • public static int MaxPathfindingTasks – позволяет устанавливать ограничение на максимальное количество задач поиска пути, работающих единовременно. По умолчанию данное свойство установлено на значение Environment.ProcessorCount - 1

Также имеется метод public static Path PrefetchPath(Vector3 _PointA, Vector3 _PointB) для создания экземпляра Path. О его применении сказано ниже.

21. Pathfinding

Для поиска пути между точками A и B необходимо:

Получить объект типа Path , вызвав метод

				
					Nav3DPathfindingManager.PrefetchPath(Vector3 _PointA, Vector3 _PointB)
				
			

Данный метод лишь создаст экземпляр Path и зарегистрирует его в Nav3D, но поиск пути осуществлен не будет.

Для созданного объекта Path вызовите метод

				
					UpdatePath(
                Vector3? _Start = null,
            	Vector3? _Goal = null,
            	int? _Timeout = null,
            	bool? _Smooth = null,
            	Action _OnSuccess = null,
            	Action<PathfindingError> _OnFail = null
)
				
			

Значения перегруженных параметров:

  • Vector3? _Start точка начала пути. Параметр необходимо передавать если вы хотите поменять точку начала пути.
  • Vector3? _Goal конечная точка пути. Параметр необходимо передавать если вы хотите поменять конечную точку пути.
  • int? _Timeout максимальное время поиска пути. В случае, если поиск пути длится дольше, произойдет выполнение колбека _OnFail, если он не равен null.
  • bool? _Smooth = null необходимо ли сглаживать найденный путь.
  • Action _OnSuccess null - колбек, который вызовется после успешного нахождения пути
  • Action _OnFail колбек, который вызовется в случае, если путь не удалось найти по какой-либо причине.

22. PathfindingError

Класс, экземпляр которого возвращается в колбек _OnFail метода Path.UpdatePath() .

Содержит два свойства:

  • public PathfindingResultCode Reason причина неудачи поиска пути
    Возможные значения свойства:
ValueОписание
SUCCEEDEDПоиск пути завершился успешно.
PATH_DOES_NOT_EXISTМежду точками не существует пути. Это возможно если хотя бы одна из точек окружена препятствиями со всех сторон.
TIMEOUTПоиск пути занял больше времени чем разрешено и был прерван.
CANCELEDПоиск пути был завершен по указанию пользователя, или по внутренней логике Nav3D.
START_POINT_INSIDE_OBSTACLEПоиск пути не удался, потому что начальная точка пути находится внутри препятствия
GOAL_POINT_INSIDE_OBSTACLEПоиск пути не удался, потому что конечная точка пути находится внутри препятствия.
UNKNOWNВнутренняя ошибка.
  • public string Msg сообщение с более подробной информацией об ошибке.

23. Path : Properties

  • public Vector3[] Trajectory { get; } - траектория найденного пути. Используйте это свойство для получения последнего найденного пути. Значение является верным, если метод UpdatePath был вызван и завершился успешно и никакие прочие свойства объекта Path не менялись после этого. Используйте свойство IsValid для проверки того, что значение верно.
  • public Vector3[] TrajectoryOriginal {get;} исходная найденная траектория.
  • public Vector3[] TrajectoryOptimized {get;} оптимизированная найденная траектория.
  • public Vector3[] TrajectorySmoothed { get; } сглаженная траектория найденного пути. Значение будет отличаться от TrajectoryOptimized, если свойство Smooth имеет значение true.
  • public Bounds Bounds { get; } объем пространства, который занят найденным путем.
  • public Vector3 Start { get; set; } точка начала пути.
  • public Vector3 Goal { get; set; } конечная точка пути.
  • public int SmoothRatio { get; set; } - количество проходов сглаживания по отношению к минимальному размеру ячейки графа поиска, по умолчанию установлено как 3. Не рекомендуем увеличивать это значение без необходимости, так как это повлечет рост времени поиска пути, в случае если сглаживание включено (свойство Smooth == true).
  • public int Timeout { get; set; } - максимальное время поиска пути. В случае, если поиск пути длится дольше, произойдет выполнение колбека Action<PathfindingError> _OnFail , если он был передан в UpdatePath().
  • public bool Smooth { get; set; } нужно ли производить сглаживание пути.
  • public bool IsValid { get; } - является ли текущее значение Trajectory не null и верно ли оно для текущих значений остальных свойств. Например если вы вызвали UpdatePath(), а после его успешного завершения изменили значение свойства Start, то путь уже не будет являться актуальным и IsValid будет равно false.
  • public bool IsPathUpdating { get; } находится ли экземпляр Path в процессе поиска пути.

24. Completing dealing with the Path Object

После того как вы завершили работу с объектом Path, необходимо вызвать метод Path.Dispose() method. This is necessary to remove the path from the obstacle storage. Otherwise, when adding / updating / removing any obstacle, the path will be checked for the need to update, which will take up computing resources.

25. Usage example

Ниже приведен пример использования объекта Path для нахождения пути между А и В

				
					using Nav3D.API;
using System;
using UnityEngine;

class YourGameScenario : MonoBehaviour
{
    private void Start()
    {
        Nav3DManager.InitNav3D(1f);

        Vector3 a = new Vector3(-10, 10, 10);
        Vector3 b = new Vector3(-10, 10, 10);

        FindPath(a, b, PrintPath);
    }

    //your method for pathfinding from A to B
    void FindPath(Vector3 _A, Vector3 _B, Action<Vector3[]> _OnPathFound)
    {
        //create Path instance
        Path path = Nav3DPathfindingManager.PrefetchPath(_A, _B);

        //perform pathfinding from _A to _B
        path.UpdatePath(_OnSuccess: () =>
        {
            //notify that path was found
            UnityEngine.Debug.Log("Path successfully found!");

            //invoke callback
            _OnPathFound(path.Trajectory);

            //finish work with Path instance
            path.Dispose();
        });
    }

    void PrintPath(Vector3[] _Path)
    {
        for (int i = 0; i < _Path.Length - 1; i++)
            UnityEngine.Debug.LogError($"Path point {i}: {_Path[i]}");
    }
}
				
			

26. Nav3DAgentDescription

				
					Nav3DAgentDescription
				
			

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

После создания описания в инспекторе вы увидите следующий список групп параметров:

Кнопка Set default parameters ускорит настройку всех параметров, выставив их значения по умолчанию.

Приведем пояснение назначения параметров и их допустимые величины.

Behavior

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Behavior Type
Behavior Type
enum BehaviorType
DEFAULT, YIELDING, INDIFFERENT

Описывает тип поведения агента.

  • Агент может вести себя стандартным образом (DEFAULT), т.е.искать путь, за тем следовать по нему, или выполнять любые другие указания пользователя. Поведение агента будет полностью определяться его описанием.
  • Агент может иметь уступающее поведение (YIELDING). Он не сможет выполнять команды пользователя и не будет иметь собственных задач.
    (При попытке вызова методов движения к цели будет выброшено исключение.) Его единственной заботой будет уступать дорогу агентам поблизости. С ними он будет взаимодействовать с помощью механизма Local avoidance, в соответствии со своими параметрами скорости и радиуса.
  • Также агент может быть безразличным (INDIFFERENT).
    Агент не будет делать ничего, просто будет находиться на месте. Другие агенты смогут обходить его при выполнении локального уклонения. Попытка вызова методов следования к цели с таким типом поведения повлечет выброс исключения, по аналогии с YIELDING.
Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Motion Navigation Type
MotionNavigationType
enum MotionNavigationType
GLOBAL, GLOBAL_AND_LOCAL, LOCAL

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

  • Используя глобальный путь (GLOBAL). В таком случае агент выполнит поиск пути и будет двигаться по нему, невзирая на внешние условия (другие агенты, находящиеся слишком близко края препятствий).
  • С использованием только локального уклонения (LOCAL). Агент не будет использовать поиск пути для движения, а будет двигаться к цели по прямой, попутно уклоняясь от других агентов и препятствий, когда они встречаются на курсе движения. Разумеется не всякое препятствие может быть преодолено таким образом.
  • Наконец, агент может осуществлять движение к цели используя поиск пути и локальное уклонение одновременно (GLOBAL_AND_LOCAL). Агент будет двигаться по найденному пути и уклоняться от подошедших близко других агентов, а также находящихся слишком близко препятствий.

Radius

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Radius
Radius
float
>0

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

Speed

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Speed
Speed
float
>0

Скорость движения агента. Можете менять ее в любой момент времени, если потребуется.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Max Speed
Max Speed
float
>Speed

Максимальная скорость агента. Этот параметр нужен для выполнения локального уклонения. В некоторых ситуациях агенту может потребоваться ускориться, чтобы избежать столкновения. Если значение будет слишком велико, то порой агент может двигаться рывками при выполнении локального уклонения. Так что советуем выставлять максимальную скорость чуть больше чем Speed(больше менее чем в полтора раза).

Для вашего удобства в инспекторе имеется два способа установления этого значения. С помощью конкретного значения. Либо с помощью множителя для Speed. Советуем использовать множитель, для того чтобы максимальная скорость зависела от Speed агента. Тогда MaxSpeed будет меняться вслед за изменением Speed

Local avoidance

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
ORCA Tau
ORCATau
float
>0

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

Лучше понять его смысл вам поможет оригинальная статья по ORCA Reciprocal n-body Collision Avoidance. Jur van den Berg, Stephen J. Guy, Ming Lin, and Dinesh Manocha

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Agents considered number limit
Agents considered number limit
bool
true, false

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

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Agents number
ConsideredAgentsNumberLimit
int
>=1

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

Поиск пути

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Pathfinding timeout (ms)
PathfindingTimeout
int
>0

Максимальное время, отведенное на выполнение поиска пути. При превышении этого времени поиск(pathfinding) будет отменен, и будет вызван колбек _OnPathfindingFail.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Smooth the path
SmoothPath
bool
true, false

Нужно ли сглаживать найденный путь.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Samples per min bucket volume
SmoothRatio
int
>0

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

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Auto-update path on stagnant behavior
AutoUpdatePath
bool
true, false

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

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

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Auto-update cooldown (ms)
PathAutoUpdateCooldown
int
>=1

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

Motion

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Target reach distance
TargetReachDistance
float
>=0

Расстояние достижения цели. Когда вы отдаете агенту приказание достичь какой-либо цели, цель будет считаться достигнутой, когда pivot агента совпадет с координатой цели (расстояние между ними будет равным 0). Это может быть неудобно, если целью является объект с мешем, допустим планета в космосе. В таком случае выставьте этот параметр равным сумме радиуса видимой части агента и радиуса планеты. Тогда агент остановится, достигнув поверхности планеты.

Параметр Свойство в Nav3DAgentDescription Тип Диапазон значений
Max rotation in degrees per fixed update tick
MaxAgentDegreesRotationPerTick
float
>=0

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

Velocity blending

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

  1. Вектор скорости следования по пути.
  2. Вектор скорости уклонения от агентов.
  3. Вектор скорости уклонения от препятствий.

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

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

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

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

Все веса задаются типом float и должны иметь значение более 0

Соответствующие названия свойств в Nav3DAgentDescription:

  • PathVelocityWeight
  • PathVelocityWeight1
  • PathVelocityWeight2
  • AgentsAvoidanceVelocityWeight
  • AgentsAvoidanceVelocityWeight1
  • AgentsAvoidanceVelocityWeight2
  • ObstacleAvoidanceVelocityWeight
  • ObstacleAvoidanceVelocityWeight1
  • ObstacleAvoidanceVelocityWeight2

Debug

Use Log UseLog bool Whether it is necessary to log agent work processes. true, false
Log records count
LogSize
int
Agent log size.
>0

26.1 Randomly generated parameters

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

Существует два типа распределения: непрерывное равномерное и гауссово. Первый делает то же самое, что и UnityEngine.Random.Range(a, b). Тип распределения по Гауссу можно описать следующим образом: большинство значений будет выбрано примерно из середины диапазона, но будет несколько значений, которые находятся ближе к концам диапазона.

Ниже приведен пример установки радиуса для диапазона [0.1, 0.3] в соответствии с распределением Гаусса.

  • Все классы ассета, необходимые вам для работы, содержатся в пространстве имен Nav3D.API.

26.2 Creating and configuring an agent description from code

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

Для этого вам нужно создать переменную типа Nav3DAgentDescription, затем применить ее к агенту. Правильный способ создания описание - обращение к свойству Nav3DAgentDescription.DefaultDescription .

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

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

				
					void ConfigureAgentDescription()
{
   Nav3DAgent myAgent = GetComponent<Nav3DAgent>();

   //create Nav3DAgentDescription instance with default parameters
   Nav3DAgentDescription myDescription = Nav3DAgentDescription.DefaultDescription;

   //set the parameters you want
   myDescription.Radius = 1.2f;
   myDescription.MotionNavigationType = MotionNavigationType.LOCAL;
  
   //apply the description to an agent
   myAgent.SetDescription(myDescription.GetDescriptionVariant());
}
				
			

27. Nav3DAgent

Как упоминалось выше, вы можете использовать скрипт Nav3DAgent в качестве компонента для вашего игрового объекта и получить к нему доступ через GetComponent() или вы можете создать класс-наследник, который лично мы считаем гораздо более удобным.

Для работы с Nav3DAgent были реализованы следующие публичые методы:
  • Применяет описание к вашему агенту.
				
					public void SetDescription(Nav3DAgentDescription _Description)
				
			
  • Команда двигаться к точке. Любая действующая команда будет прервана, агент начнет движение к точке, в соответствии с параметрами его описания.
				
					public void MoveTo(Vector3 _Point, Action _OnReach = null, Action<PathfindingError> _OnPathfindingFail = null)
				
			
  • Добавить точку следования в очередь. Таким образом может быть добавлено сколько угодно точек следования. Если агент выполняет команду FollowTarget, то она прервется и он начинает выполнять команду следование к точке.
				
					public void MoveToEnqueue(Vector3 _Point, Action _OnReach = null, Action<PathfindingError> _OnPathfindingFail = null)
				
			
  • Команда преследовать движущийся Transform.

				
					public void FollowTarget(
            Transform _Target,
            float _OffsetToleranceUpdate,
            float _DistToReach = 0,
            Action _OnReach = null,
            Action _OnCancel = null,
            Action<PathfindingError> _OnPathfindingFail = null
        )
				
			
  • Transform _Target – Transform, который необходимо преследовать.
  • float _OffsetToleranceUpdate – величина минимального смещения цели, которое приведет к обновлению пути к цели. Если ваш агент имеет параметр Motion Navigation Type описания установленным в значение GLOBAL, или GLOBAL_AND_LOCAL, то каждый раз при смещении преследуемого Transform более чем на _OffsetToleranceUpdate произойдет обновление пути.
  • If your agent has the Motion Navigation Type of the description set to GLOBAL, or GLOBAL_AND_LOCAL, then each time the pursued Transform is offset by more than _OffsetToleranceUpdate , it will cause the path to be recalculated.
  • float _DistToReach – расстояние до цели, приблизившись на которое цель будет считаться достигнутой. Хотим заметить, что в случае когда агенту приходится искать глобальный путь, цель может никогда не быть достигнутой, поскольку достигнув конца пути может оказаться, что цель за прошедшее время сдвинулась. Таким образом условие совпадения координат преследуемой цели и агента может быть труднодостижимым.
  • Action _OnReach – Делегат, который будет выполнен, когда цель будет успешно достигнута.
  • Action _OnCancel – делегат, выполняемый в случае, если ссылка на преследуемый Transform стала null, либо missing.
  • Прерывает выполнение текущей команды.
				
					public void Stop()
				
			
  • Позволяет получить список агентов внутри радиуса _Radius от агента. Вызов этого метода по событию Update, может негативно сказаться на производительности, поэтому рекомендуется не вызывать его слишком часто (например, нескольких раз в секунду должно быть достаточно для любых игровых сценариев).
				
					public List<Nav3DAgent> GetAgentsInRadius(float _Radius, Predicate<Nav3DAgent> _Predicate = null)
				
			
  • Визуализирует агента с помощью Gizmos. Может быть полезно для отладки в Editor
				
					public void Visualize(bool _DrawRadius = true, bool _DrawPath = true, bool _DrawVelocities = true)
				
			

Вы можете найти примеры использования Nav 3D Agent в демонстрационных сценах, расположенных в папке Demo ресурса.

28. Nav3DAgent : Debug

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

28.1 Debug drawing

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

1. Визуализация радиуса действия агента.

2. Визуализация векторов скорости агента.

3. Визуализация пути, по которому следует агент.

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

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

6. Все.

28.2 Agent log

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

Agent log

In the second section, you can get the contents of the agent log by clicking on ...

Debug drawing

In the first section, you can visualize the agent and his nearest environment. T...

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

All Nav3DAgentDescription parameters configured in the description inspector c...

Nav3DManager

Nav3DManager is a helper static class. Can be useful for checking whether Nav3D ...

Nav3DInitializer

To use Nav3D in playmode, you need to initialize it. The Nav3DInitializer compon...

Nav3DObstacleLoader

To use the possibility of pre-baking obstacles on the scene in editor mode and t...

Nav3DObstacle

To manage obstacles on the scene, the Nav3DObstacle component is designed. Attac...

Сочетания препятствий

We will call obstacles with the selected processing mode “Runtime” as runtim...

Операции с препятствиями

Regardless of the obstacle processing mode, the processing procedure consists of...

Немного глубже о препятствиях

As already described above, in order for an obstacle to be taken into account du...

Nav3DAgent : Debug

The Nav3DAgent and its inheritors have an inspector that provides several useful...

Nav3DAgent

As mentioned above, you can use the Nav3DAgent script as a component for your ga...

Chat Icon Close Icon
ru_RURussian