Производительность навигации
Разделить стоимость поиска, обновления карты и запекания; измерить запрос.
Оптимизируйте измеренный участок работы. Уменьшение числа полигонов не исправит повторное назначение цели каждому агенту на каждом кадре, а фоновое запекание не устраняет стоимость разбора исходной геометрии.
Разделите пять затрат
| Этап | Что измерять | Первое изменение для сравнения |
|---|---|---|
| Разбор исходной геометрии | Время вызова разбора, отдельно от запекания. | Ограничить область и типы источников; использовать простую коллизию или упрощённую геометрию. |
| Запекание | Время до готового результата, отдельно от публикации карты. | Проверить размер области, CellSize, CellHeight и требования к проходам. |
| Обновление карты | Серверную обработку при изменении регионов и связей. | Не менять неподвижную геометрию постоянно; уменьшить число лишних рёбер. |
| Поиск пути | Время вызова запроса и число запросов за кадр. | Убрать лишние запросы; проверить достижимые и недостижимые цели. |
| Избегание | Серверное время при включённом/выключенном избегании в одинаковом сценарии. | Включать его только нужным агентам; проверить число соседей и горизонт прогнозирования. |
Серверное время не равно времени поиска
Performance.GetMonitor(Performance.Monitor.TimeNavigationProcess) возвращает секунды: для миллисекунд умножьте на 1000. В 1.0.0 основной цикл накапливает максимум длительности обработки 2D- и 3D-навигации за интервал обновления статистики, примерно секунду. Это не среднее за кадр и не измерение последнего MapGetPath().
Синхронный поиск по опубликованной карте измеряйте вокруг самого вызова. Следующий файл содержит также снимок счётчиков 2D:
using Reckfell;
public static class NavigationMeasurements
{
// Invoke after the map has published its geometry; this measures one synchronous query.
public static (Vector2[] Path, ulong Microseconds) Query2D(
Rid map, Vector2 start, Vector2 target, uint layers = 1)
{
ulong before = Time.GetTicksUsec();
Vector2[] path = NavigationServer2D.MapGetPath(map, start, target, true, layers);
return (path, Time.GetTicksUsec() - before);
}
public static (int Maps, int Regions, int Polygons) Snapshot2D() => (
NavigationServer2D.GetProcessInfo(NavigationServer2D.ProcessInfo.ActiveMaps),
NavigationServer2D.GetProcessInfo(NavigationServer2D.ProcessInfo.RegionCount),
NavigationServer2D.GetProcessInfo(NavigationServer2D.ProcessInfo.PolygonCount));
}Вызывайте Query2D с действительной картой после публикации нужной геометрии. Microseconds — прошедшее время этого вызова в микросекундах, включая обвязку и возможное ожидание; короткий вызов может дать 0 из-за разрешения таймера. Возвращённый Path проверяйте как обычно: наличие точек не доказывает достижение цели.
Для сравнения выполните прогрев, затем серию одинаковых запросов без печати внутри измеряемого участка. Зафиксируйте сборку, оборудование, карту, маску, число запросов и распределение результатов. Сравните медиану и высокие перцентили. Проверенный здесь пример подтверждает работу обвязки, а не скорость движка или преимущество над другим движком.
Не сбрасывайте маршрут без причины
Повторное присваивание TargetPosition, даже того же значения, запрашивает перепланирование. Для движущейся цели храните последнее отправленное положение и обновляйте его при значимом смещении или по расписанию. Порог выбирайте по игровым требованиям; слишком большой порог ухудшит преследование.
Не убирайте регулярное следование пути: ограничивайте назначение новой цели, а не необходимый вызов GetNextPathPosition() в физическом цикле. Для большой группы распределяйте новые назначения по кадрам, сохраняя допустимую задержку реакции.
IsTargetReachable() может обновить навигацию агента. Это не отдельный гарантированно дешёвый предварительный тест. Если путь всё равно нужен, используйте его результат и конечную точку вместо второго независимого поиска.
Уменьшайте геометрию без потери проходов
Для запекания не нужна вся детализация видимой модели. Простой источник уменьшает объём разбора; изменение настроек запекания затем проверяйте на узких проходах, ступенях и границах регионов. Увеличение CellSize/CellHeight в 3D уменьшает разрешение воксельной сетки, но может убрать нужный проход. Требуемые размеры персонажа должны остаться корректными.
Не используйте масштабирование как замену упрощению модели: число исходных треугольников от этого не уменьшается. При этом утверждение «масштаб всегда игнорируется навигацией» неверно: в проверенном примере 2D масштаб региона изменил границы маршрута. Согласуйте исходные размеры, преобразования и параметры карты; запекание.
При дорогом соединении регионов сравните EdgeMergeCount, EdgeConnectionCount и EdgeFreeCount до/после изменения. Совпадающие ключи сетки и дополнительные связи по близости — разные механизмы. Не увеличивайте размер ячейки или допуск вслепую: можно соединить участки, которые должны оставаться раздельными. Проверенные примеры соединений.
Как подтвердить улучшение
- Сохраните воспроизводимый сценарий и исходные измерения.
- Измените одну причину затрат.
- Повторите измерение с теми же условиями и проверьте корректность путей.
- Включите недостижимую цель и массовое обновление агентов: лёгкий одиночный маршрут может скрыть проблему.
- Проверьте время кадра целиком. Быстрый поиск не гарантирует быстрые физику, анимацию и рендеринг.
Обвязка поиска, маска слоёв и сводные счётчики выполнены на 1.0.0. Методика измерения не является опубликованным бенчмарком; нагрузка толпы, асинхронное запекание и стоимость визуализации здесь не измерены.