tags: [vr-ar]
Программирование ИИ врагов для навигации в 3D пространстве игр
NavMeshAgent застрял в углу. Или ходит по кругу вокруг препятствия, когда мог бы обойти за секунду. Или телепортируется через стену при смене сцены. Проблемы навигации ИИ в 3D — не «что-то пошло не так», а конкретные архитектурные решения, которые либо учитывают ограничения NavMesh, либо нет.
В VR специфика острее: игрок физически находится в пространстве игры, передвигается сам, и враги должны реагировать на это движение немедленно. Враг, который «подумал» 200 мс перед поворотом, в VR заметен как никогда.
Типичные отказы NavMeshAgent и их причины
Застревание на стыке NavMeshSurface. Если сцена использует несколько NavMeshSurface (разные области, разные уровни), агент застревает при переходе между ними. Причина — стыки NavMesh не сшиваются автоматически. Нужно либо использовать единую поверхность с NavMeshLink для мостов между ними, либо явно добавлять NavMeshLink компоненты на точках перехода.
Агент не обновляет путь при движении игрока. SetDestination вызывается один раз при обнаружении игрока, и агент идёт к той точке, где игрок был. Нужен periodic path update — каждые 0.2–0.5 секунды вызывать SetDestination с текущей позицией цели. Слишком часто — CPU overhead от постоянного пересчёта пути. Слишком редко — враг «опаздывает». Компромисс: обновлять только если цель переместилась на destinationChangeThreshold метров.
Агент проходит сквозь других агентов. NavMeshAgent.radius — это радиус для NavMesh obstacle avoidance, но он работает только с NavMeshObstacle и другими агентами. Если агент имеет avoidancePriority ниже другого, он уступает и может «вдавливаться» в объекты. Расставляем приоритеты: рядовые враги — 50, боссы — 20, игрок — 10 (более высокий приоритет = меньшее число = другие уступают).
Steering Behaviour поверх NavMesh
NavMesh даёт глобальный путь от A до B. Но движение по этому пути — отдельная задача. Стандартный NavMeshAgent.steeringTarget — следующая точка пути — может давать угловатое движение: агент делает резкий поворот на 90° вместо плавного.
Решение: отключить NavMeshAgent.updateRotation, и вращать агента самостоятельно через Quaternion.RotateTowards с угловой скоростью, соответствующей характеру персонажа. Медленный зомби — 60°/сек. Быстрый боец — 180°/сек. Это сразу делает движение убедительнее.
Для более сложного поведения — Steering Behaviours поверх NavMesh: Seek (к цели), Flee (от цели), Separation (расходиться от сородичей). Separation особенно важен для группы врагов в VR — без него они все телепортируются в одну точку, что выглядит нереалистично. Separation реализуется как дополнительная сила, добавляемая к желаемой скорости: берём всех агентов в радиусе separationRadius, суммируем векторы «от каждого», нормализуем и добавляем как смещение к NavMeshAgent.velocity.
Behaviour Tree vs State Machine для VR-врагов
State Machine (через Animator Controller с параметрами или через код) — проще в реализации, но плохо масштабируется. При 5+ состояниях переходы превращаются в спагетти.
Behaviour Tree (BT) — иерархическая структура задач. Враг: Selector → [Attack если дистанция < 2м → Chase если видит игрока → Patrol иначе]. Каждый узел — Sequence (все дочерние должны успеть), Selector (достаточно одного успешного), или Leaf (конкретное действие). В Unity нет встроенного BT, но есть пакет Behavior (от Unity, 2024+) или открытые решения NPBehave, Fluid Behaviour Tree.
Для VR важно: BT должны обновляться с разной частотой в зависимости от дистанции до игрока. Враг в 30 м — обновление BT раз в секунду. Враг в 3 м — каждые 100 мс. LOD-driven AI update rate через AIDirector или простой DistanceBasedUpdateRate компонент снижает CPU-нагрузку от множества активных агентов.
Пространственная осведомлённость: слух и зрение
Vision cone реализуется через Physics.OverlapSphere + угловую проверку + Linecast для проверки видимости. Стандартный подход: собрать все потенциальные цели в радиусе sightRange, отфильтровать по Vector3.Angle < fieldOfView / 2, проверить Linecast на препятствия — первый непересечённый — цель обнаружена.
Для VR-игр добавляем проверку на звуковые стимулы: игрок стреляет или бежит → создаётся SoundStimulus событие с позицией и интенсивностью → все агенты в радиусе intensity * attenuationFactor получают уведомление. Это можно реализовать через Unity Events или простой Physics.OverlapSphere из точки звука.
| Сложность ИИ | Ориентировочные сроки |
|---|---|
| Базовая навигация (NavMeshAgent + chase/patrol) | 1–2 недели |
| Behaviour Tree + группы врагов + Steering | 3–6 недель |
| Полная AI-система с восприятием и LOD | 2–4 месяца |
Стоимость рассчитывается после анализа требований к поведению врагов и числу одновременно активных агентов.





