Розробка систем навігації (NavMesh/Pathfinding) у мобільних грах
Ворог, що ходить крізь стіни, або NPC, застряглий у кутку — це не bug, це відсутність належної системи навігації. NavMesh та pathfinding вирішують конкретну проблему: надайте агентові маршрут від точки A до точки B, обходячи перешкоди, та робіть це ефективно.
NavMesh: статична геометрія навігації
NavMesh — це спрощене представлення рівня, яким агенти можуть рухатися. Будується один раз при завантаженні рівня (або запікується в редакторі заздалегідь). Unity пропонує вбудований NavMesh з компонентом NavMeshAgent. Godot має NavigationServer3D / NavigationServer2D.
Ключові параметри запікання NavMesh, що впливають на якість:
Agent Radius: 0.4 // радіус капсули—NavMesh зміщується на цю величину
Agent Height: 1.8 // для виявлення низьких проходів
Max Slope: 45° // максимальний кут підйому
Step Height: 0.4 // висота ступенів, які агент може подолати
На мобільних пристроях критично: NavMesh запікується заздалегідь в Editor, а не під час виконання. Runtime baking (через NavMeshBuilder.BuildNavMeshAsync) існує, але займає 100–500ms на слабких пристроях і створює тиск на garbage collection.
A* та його варіанти
Unity використовує вбудований A* з евристикою Euclidean distance для pathfinding на NavMesh. Це оптимально для більшості мобільних ігор. Але сценарії існують, де стандартний A* не справляється:
Динамічні перешкоди — гравець розставляє баррикади, дверь закривається. Стандартний NavMesh потребує перебудови. Рішення: NavMeshObstacle з Carve = true вирізає компонент з NavMesh під час виконання, але перерахування дороговартісне. Для частих змін, Flow Field pathfinding: розраховуйте векторне поле для цілі один раз, агенти йдуть за полем без індивідуального pathfinding.
Багато агентів, одна ціль — зомбі-ігри, tower defense. A* для кожного агента окремо з 100+ агентами вбиває продуктивність. Flow Field обраховується один раз для поля—агенти читають значення своєї комірки. O(1) на агента проти O(n log n) для A*.
// Unity: базовий розрахунок Flow Field для tile-based карт
public class FlowField {
private Vector2[,] directions;
private int width, height;
public void Calculate(Vector2Int target, bool[,] obstacles) {
// BFS від цілі, обраховуємо вартість та напрямок для кожної комірки
var costField = new int[width, height];
var queue = new Queue<Vector2Int>();
queue.Enqueue(target);
costField[target.x, target.y] = 0;
while (queue.Count > 0) {
var current = queue.Dequeue();
foreach (var neighbor in GetNeighbors(current)) {
if (!obstacles[neighbor.x, neighbor.y] &&
costField[neighbor.x, neighbor.y] == int.MaxValue) {
costField[neighbor.x, neighbor.y] = costField[current.x, current.y] + 1;
queue.Enqueue(neighbor);
}
}
}
// Заповнюємо directions на основі costField...
}
public Vector2 GetDirection(Vector2Int position) => directions[position.x, position.y];
}
Steering behaviours: плавне рухання
Pathfinding надає маршрут—список waypoints. Steering behaviours перетворюють це на плавне рухання:
- Seek / Arrive: рухатися до цілі, сповільнюватися при наближенні
- Obstacle Avoidance: обходити динамічні перешкоди через Raycast
- Separation: агенти не скупчуються в одній точці
- Cohesion: група тримається разом (для стайної поведінки)
NavMeshAgent у Unity включає базові steering behaviours. Для тоншого контролю використовуйте RVOSimulator з пакета com.unity.ai.navigation (алгоритм ORCA для взаємних velocity障碍).
Продуктивність мобільних пристроїв
Не перерахуйте шляхи кожний кадр. NavMeshAgent.SetDestination() при кожному виклику запускає новий pathfinding запит. Для переслідування гравця достатньо перерахувати кожні 0.3–0.5 секунди:
private float pathUpdateTimer = 0f;
private const float PATH_UPDATE_INTERVAL = 0.3f;
void Update() {
pathUpdateTimer += Time.deltaTime;
if (pathUpdateTimer >= PATH_UPDATE_INTERVAL) {
agent.SetDestination(player.position);
pathUpdateTimer = 0f;
}
}
LOD для навігації. Агенти поза камерою відключають NavMeshAgent та використовують спрощену телепортацію до waypoints. Увімкніть повний pathfinding тільки при попаданні в frustum.
Unity Job System для A.* Для кастомного pathfinding на великих картах, IJob + NativeArray<> зніміть з основного потоку. Burst Compiler дає ~10x прискорення для pathfinding математики.
Налагодження та візуалізація
Pathfinding складно налагоджувати без візуалізації. У режимі Editor рисуйте шлях NavMesh:
void OnDrawGizmos() {
if (agent != null && agent.hasPath) {
Gizmos.color = Color.yellow;
var corners = agent.path.corners;
for (int i = 0; i < corners.Length - 1; i++) {
Gizmos.DrawLine(corners[i], corners[i + 1]);
}
}
}
Огляд процесу
Проаналізуйте рівень: статична геометрія, динамічні перешкоди, число агентів.
Виберіть алгоритм: NavMesh + A* для типових сценаріїв, Flow Field для масових агентів.
Налаштуйте параметри NavMesh, інтегруйте з ігровою геометрією.
Реалізуйте steering behaviours, налаштуйте плавність руху.
Оптимізуйте: навігація LOD, інтервали оновлення, Job System для кастомного pathfinding.
Тестуйте на цільових пристроях з Unity Profiler.
Оцінки часових рамок
Базова навігація через NavMeshAgent для 5–10 типів агентів: 3–5 днів. Кастомна система з Flow Field, динамічними перешкодами та LOD оптимізацією: 2–4 тижні.







