Update the runtime NavMesh architecture to a DI and MessagePipe sidecar model, and add reusable agent prompt templates that capture the project's current multiplayer, WebGL, and modularity constraints.
14 KiB
TASK-0023 Runtime NavMesh Implementation Plan
Goal
Реализовать runtime NavMesh для procedural voxel world как подключаемый sidecar-модуль в отдельной assembly, без camera-driven assumptions, с совместимостью с будущей peer-host multiplayer моделью и уже внедренными VContainer + MessagePipe.
Inputs And Assumptions
- текущая test scene:
Assets/Features/VoxelWorld/Scenes/VoxelWorldTestScene.unity - основной runtime генерации мира:
Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs - в проекте уже есть
ApplicationLifetimeScopeсMessagePiperegistration - первая итерация NavMesh coverage строится вокруг player actor
- долгосрочный контракт остается
players + active NPC - один тип агента
- динамические изменения мира пока не реализуются, но контракты под них должны быть предусмотрены
- WebGL-host остается целевой платформой, поэтому базовый pipeline не зависит от потоков
Chosen Technical Direction
1. NavMesh не внедряется в VoxelWorldGenerator как internal partial-логика
Решение:
VoxelWorldGeneratorостается producer world state и chunk geometry- runtime NavMesh живет в отдельном модуле и подписывается на world contracts
Почему:
- это соответствует целевой модели feature-модулей как подключаемых подсистем
- модуль можно будет реально отключить
- world feature не будет знать детали nav scheduling и
NavMeshDatalifecycle
2. Модуль использует MessagePipe для событий, но не опирается только на сообщения
Решение:
MessagePipeиспользуется для lifecycle/invalidation notifications- reader-интерфейсы используются для чтения текущего состояния world geometry и interest points
Почему:
- message-only подход ломается на late subscription и startup ordering
- NavMesh service должен уметь стартовать позже publisher-а и восстановить актуальное состояние
3. Runtime pipeline строится через NavMeshBuilder.UpdateNavMeshDataAsync
Почему:
- это дает контроль над
NavMeshData,Bounds, build sources и budget - лучше подходит для region-based rebuild под WebGL-host, чем sample-подход с одним sliding volume
4. NavMesh строится по nav regions, а не per-chunk и не full-volume вокруг target
Выбор:
- отдельный
NavMeshDataна nav region - стартовый размер region:
2x2чанка, configurable
Почему:
- per-chunk ведет к слишком большому числу мелких build operations
- один большой moving volume слишком дорог и плохо контролируется по бюджету
- region-based rebuild дает лучший компромисс между стоимостью и связностью
5. Источники build sources берутся из chunk colliders, публикуемых world feature
Выбор:
GroundColliderдает box sourceMountainCollider.sharedMeshдает mesh source
Почему:
- не нужен scene-wide scanning
- не требуется отдельная nav-only геометрия на первом этапе
- это наиболее близкое к gameplay представление walkable/non-walkable world geometry
Target Module Boundaries
Assembly Layout
Assets/Features/VoxelWorld/Contracts/VoxelWorld.Contracts.asmdefAssets/Features/VoxelWorld/Runtime/VoxelWorld.Runtime.asmdefAssets/Features/VoxelWorldNavMesh/Runtime/VoxelWorld.NavMesh.Runtime.asmdef
Почему так:
Contractsфиксируют стабильную внешнюю границуRuntimeреализует мир и публикует contractsVoxelWorld.NavMesh.Runtimeостается optional consumer-модулем
Contracts To Add
Reader Interfaces
IChunkNavGeometryReaderIWorldInterestReader
Минимальная ответственность:
IChunkNavGeometryReaderумеет вернуть текущую nav-геометрию чанка и список уже загруженных чанковIWorldInterestReaderумеет вернуть текущую primary interest point
DTO / Contracts
ChunkNavGeometry
Состав DTO:
Vector2Int CoordTransform RootBoxCollider GroundColliderMeshCollider MountainColliderint Version
Примечание:
- DTO должен содержать только то, что реально нужно для NavMesh source collection
- private nested types
VoxelWorldGeneratorне должны утекать наружу
Message Types
ChunkNavGeometryReadyMessageChunkNavGeometryRemovedMessageWorldInterestChangedMessage- позже:
ChunkWalkabilityChangedMessageили аналогичный delta-invalidating message
Правило:
- сообщения несут ключ и минимальные данные invalidation
- тяжелое актуальное состояние читается через reader interfaces
Required Changes In VoxelWorldGenerator
1. Перестать быть единственной точкой nav logic
VoxelWorldGenerator должен только:
- генерировать и стримить чанки
- создавать/apply collider mesh
- публиковать world contracts
Он не должен:
- владеть dirty nav region queue
- владеть
NavMeshData - напрямую запускать
NavMeshBuilder
2. Реализовать reader interfaces
IChunkNavGeometryReaderIWorldInterestReader
3. Публиковать сообщения после world lifecycle changes
Нужно публиковать:
ChunkNavGeometryReadyMessageпосле фактического применения collider meshChunkNavGeometryRemovedMessageперед уничтожением чанкаWorldInterestChangedMessageпри смене actor-level interest point
4. Убрать каноническую зависимость от Camera.main
Для первой итерации допускается scene wiring через explicit target reference, но не через runtime fallback на Camera.main как на архитектурную норму.
NavMesh Module Structure
Рекомендуемые файлы:
Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshService.csAssets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshTypes.csAssets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshConfig.csAssets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshModule.cs
Дополнительно, если нужен bridge для scene binding на переходном этапе:
Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshEntry.cs
DI Composition
Registration Model
VoxelWorld регистрирует:
VoxelWorldGeneratorкак реализацию reader interfaces- publishers соответствующих world messages
VoxelWorldNavMesh регистрирует:
VoxelWorldNavMeshService- его subscribers
- config instance
Important Rule
- feature-код не должен использовать
GlobalMessagePipeкак основную integration point IPublisher<T>иISubscriber<T>должны приходить через DI
Почему:
- это сохраняет тестируемость
- не создает скрытых runtime-зависимостей
- лучше соответствует модульному подключению через
LifetimeScope
NavMesh Service Responsibilities
- подписаться на world lifecycle messages
- на старте получить snapshot уже загруженных чанков через
IChunkNavGeometryReader - построить initial set dirty regions
- поддерживать
NavMeshDataпо регионам - собирать
NavMeshBuildSourceиз chunk colliders - запускать throttled
UpdateNavMeshDataAsync - переоценивать build priority относительно current interest point
- удалять region data, когда она выходит из активного диапазона и становится пустой
Region Runtime Data
Dictionary<Vector2Int, NavRegionRuntime> navRegionsQueue<Vector2Int> dirtyNavRegionsHashSet<Vector2Int> queuedNavRegions
NavRegionRuntime должен хранить:
NavMeshData NavMeshDataNavMeshDataInstance InstanceAsyncOperation ActiveBuildint Versionbool BuildRequestedWhileRunningBounds BuildBounds
New Config Settings
Добавить в NavMesh module config:
int navRegionSizeInChunks = 2int maxConcurrentNavMeshBuilds = 1int maxNavMeshBuildsPerFrame = 1float navBoundsHorizontalPaddingfloat navBoundsVerticalPaddingint navWarmupRadiusInRegions
Примечание:
- для первой итерации
maxConcurrentNavMeshBuildsдолжен оставаться1
Build Flow
Initial Sync
- Сервис стартует.
- Через
IChunkNavGeometryReaderполучает список уже загруженных чанков. - Помечает соответствующие nav regions dirty.
- Через
IWorldInterestReaderполучает текущую точку интереса. - Запускает scheduler.
Incremental Update
- Приходит
ChunkNavGeometryReadyMessage. - Сервис читает актуальную geometry через
IChunkNavGeometryReader. - Помечает nav region dirty.
- Если chunk расположен на границе region, дополнительно маркирует соседний region.
Removal
- Приходит
ChunkNavGeometryRemovedMessage. - Сервис помечает соответствующий region dirty.
- Если region опустел и вышел из активной зоны, удаляет
NavMeshDataInstance.
Interest Update
- Приходит
WorldInterestChangedMessage. - Сервис обновляет current interest point.
- Scheduler пересчитывает порядок rebuild и warmup regions.
Source Collection Rules
Для каждого затронутого region:
- собрать build sources только из чанков региона и соседнего margin
- использовать только известную geometry из reader interface
- не сканировать произвольные объекты сцены
Для каждого chunk geometry:
- добавить
NavMeshBuildSourceShape.BoxизGroundCollider - добавить
NavMeshBuildSourceShape.MeshизMountainCollider.sharedMesh, если mesh не пустой
Performance Rules
- не делать full-scene bake
- не пересобирать NavMesh синхронно через
BuildNavMesh()на gameplay path - не строить систему в расчете на обязательный background threading
- держать максимум один активный region build
- rebuild запускать только после фактического применения collider mesh
- события из
MessagePipeне должны тащить heavy geometry payload, только invalidation keys - scheduler должен быть bounded и deterministic по budget
Verification Plan
Functional
- Запустить
VoxelWorldTestScene. - Убедиться, что NavMesh module можно отключить и world generation продолжает работать.
- Подключить NavMesh module и проверить появление walkable NavMesh на уже загруженных чанках.
- Проверить, что agent из AI Navigation samples строит путь по поверхности.
- Проверить unload чанков: старый NavMesh не должен оставлять висячие walkable islands.
Integration
- Проверить старт сервиса после world generator: missed events не должны ломать initial sync.
- Проверить, что модуль работает только через DI-injected
MessagePipeи reader interfaces. - Проверить, что отключение регистрации
VoxelWorldNavMeshне ломает world feature.
Performance
- Быстро перемещать actor target по миру.
- Снять показатели:
- active nav regions
- queued dirty regions
- builds started
- stale rebuilds dropped
- worst-frame rebuild spikes
Explicit Non-Goals For This Iteration
NavMeshObstaclecarving- multi-agent bake
- networked NavMesh replication
- ownership migration для чанков или NPC
- финальная multiplayer interest model вокруг всех actors
- прямые зависимости NavMesh feature от
VoxelWorldGeneratorinternals
Execution Order
- Добавить contracts assembly для world-to-navmesh integration.
- Добавить message types и reader interfaces.
- Адаптировать
VoxelWorldGeneratorпод публикацию world contracts и messages. - Создать отдельную assembly и runtime module
VoxelWorld.NavMesh.Runtime. - Реализовать
VoxelWorldNavMeshServiceс initial sync через readers и incremental updates черезMessagePipe. - Реализовать region scheduler и
NavMeshBuilder.UpdateNavMeshDataAsync. - Подключить модуль через DI registration.
- Провести ручную проверку и зафиксировать фактические perf observations.