document runtime navmesh architecture decisions
Fix the authority and world-state assumptions before implementation so runtime NavMesh work can stay consistent with the project's future DI and peer-host multiplayer model.
This commit is contained in:
@@ -0,0 +1,219 @@
|
|||||||
|
# MVP World, Authority And Runtime NavMesh
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Этот документ считается каноническим для решений по детерминированному миру, authority model и runtime NavMesh, пока его явно не заменят более новым архитектурным решением.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Зафиксировать долгосрочные решения для MVP, чтобы downstream-задачи по FishNet, worldgen, AI и persistence не уехали в разные стороны.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- deterministic voxel world generation
|
||||||
|
- authority model для session gameplay
|
||||||
|
- runtime NavMesh в procedural world
|
||||||
|
- риски WebGL-host режима
|
||||||
|
|
||||||
|
## Fixed Decisions
|
||||||
|
|
||||||
|
### 1. Базовый мир генерируется детерминированно и локально на каждом peer
|
||||||
|
|
||||||
|
Решение:
|
||||||
|
- базовая геометрия мира не стримится от хоста по сети
|
||||||
|
- каждый peer генерирует чанк локально из одинакового `seed`, одинакового `VoxelWorldConfig` и одинаковой версии world rules
|
||||||
|
|
||||||
|
Почему выбрано:
|
||||||
|
- для WebGL и peer-host модели это минимизирует сетевой трафик
|
||||||
|
- убирает постоянную сетевую репликацию геометрии чанков
|
||||||
|
- снимает с хоста роль единственной точки генерации базового мира
|
||||||
|
- хорошо сочетается с уже существующим `VoxelWorldGenerator`, который строит чанк из deterministic inputs
|
||||||
|
|
||||||
|
Почему не выбран host-generated world streaming:
|
||||||
|
- хост получал бы лишнюю CPU-нагрузку на генерацию и лишнюю сетевую нагрузку на раздачу чанков
|
||||||
|
- late join и догрузка дальних областей становились бы тяжелее по сети
|
||||||
|
- это хуже укладывается в бюджет WebGL-host
|
||||||
|
|
||||||
|
Последствия:
|
||||||
|
- `seed`, world config и их версия становятся частью session handshake
|
||||||
|
- любое расхождение по config/version между peers недопустимо и должно считаться protocol drift
|
||||||
|
|
||||||
|
### 2. Host остается authoritative для NPC, AI и другого gameplay state
|
||||||
|
|
||||||
|
Решение:
|
||||||
|
- NPC симулируются на хосте
|
||||||
|
- pathfinding NPC, агро, боевые решения и каноническое положение NPC принадлежат хосту
|
||||||
|
- клиенты получают состояние NPC по сети и могут делать только визуальное сглаживание
|
||||||
|
|
||||||
|
Почему выбрано:
|
||||||
|
- NPC влияют на бой, урон, столкновения и progression, значит их нельзя отдавать в authority случайному клиенту
|
||||||
|
- это радикально снижает риск читов и эксплуатационных багов
|
||||||
|
- упрощает late join, reconnect и дебаг сетевой симуляции
|
||||||
|
|
||||||
|
Почему не выбран client-owned NPC:
|
||||||
|
- ownership у первого встретившего игрока нестабилен при совместной игре
|
||||||
|
- миграция owner во время боя ломает воспроизводимость path state, aggro state и hit timing
|
||||||
|
- возрастает риск desync и эксплойтов
|
||||||
|
- резко усложняется отладка и сопровождение
|
||||||
|
|
||||||
|
Последствия:
|
||||||
|
- `client-authority` допустим только для ввода игрока и только при отдельной валидации на сервере
|
||||||
|
- для NPC authority migration в MVP не используется
|
||||||
|
|
||||||
|
### 3. У чанков нет owner и нет chunk ownership migration
|
||||||
|
|
||||||
|
Решение:
|
||||||
|
- чанк не закрепляется за конкретным игроком как за владельцем
|
||||||
|
- базовый чанк является общей детерминированной сущностью мира, а не network-owned объектом
|
||||||
|
|
||||||
|
Почему выбрано:
|
||||||
|
- при deterministic world generation ownership чанка не дает полезного выигрыша
|
||||||
|
- chunk ownership добавляет coordination cost, миграцию ответственности и новые классы сетевых гонок без пользы для MVP
|
||||||
|
- это плохо совместимо с late join и с будущими world deltas
|
||||||
|
|
||||||
|
Почему не выбран owner-per-chunk:
|
||||||
|
- первый увидевший чанк игрок не является надежным authority source
|
||||||
|
- потребуется сложная логика передачи владения при сближении игроков и при disconnect
|
||||||
|
- любые расхождения по владельцу чанка приводят к hidden state drift
|
||||||
|
|
||||||
|
Последствия:
|
||||||
|
- изменения чанка в будущем пойдут не через owner migration, а через authoritative world deltas от хоста
|
||||||
|
|
||||||
|
### 4. Runtime NavMesh строится локально на каждом peer по фактической локальной геометрии чанка
|
||||||
|
|
||||||
|
Решение:
|
||||||
|
- NavMesh не реплицируется по сети как data blob
|
||||||
|
- каждый peer строит NavMesh у себя локально из актуальной локальной геометрии мира
|
||||||
|
- NavMesh всегда считается производным кэшем от world state, а не каноническим состоянием сессии
|
||||||
|
|
||||||
|
Почему выбрано:
|
||||||
|
- NavMesh data тяжелая и плохо подходит для сетевой репликации в peer-host модели
|
||||||
|
- при deterministic base world и одинаковых world deltas peers могут независимо прийти к одинаковой walkable topology
|
||||||
|
- это сохраняет сеть для gameplay state, а не для производных навигационных артефактов
|
||||||
|
|
||||||
|
Почему не выбран network-streamed NavMesh:
|
||||||
|
- лишний трафик и высокая сложность синхронизации
|
||||||
|
- плохая масштабируемость для догрузки чанков и late join
|
||||||
|
- NavMesh все равно пришлось бы пересобирать при локальных изменениях геометрии
|
||||||
|
|
||||||
|
Последствия:
|
||||||
|
- каноничность gameplay не должна зависеть от клиентского NavMesh
|
||||||
|
- client NavMesh используется для локальных потребностей, но authoritative decisions по NPC остаются у хоста
|
||||||
|
|
||||||
|
### 5. Будущие изменения проходимости мира передаются как authoritative world deltas
|
||||||
|
|
||||||
|
Решение:
|
||||||
|
- базовый мир идет из deterministic generation
|
||||||
|
- любые будущие баррикады, спеллы, разрушаемость, carve и другие изменения мира передаются как authoritative deltas от хоста
|
||||||
|
- после применения delta каждый peer локально перестраивает затронутые nav regions
|
||||||
|
|
||||||
|
Почему выбрано:
|
||||||
|
- это отделяет immutable base generation от mutable session state
|
||||||
|
- обеспечивает late join: новому игроку можно отдать base seed/config и журнал world deltas
|
||||||
|
- не требует вводить ownership migration для чанков
|
||||||
|
|
||||||
|
Почему не выбран fully local mutable world:
|
||||||
|
- local-first изменения мира не могут быть каноническими в кооперативной сетевой игре
|
||||||
|
- конфликтуют с античитом, late join и persistence
|
||||||
|
|
||||||
|
Последствия:
|
||||||
|
- NavMesh pipeline обязан уметь маркировать локальные nav regions как dirty после world delta
|
||||||
|
|
||||||
|
### 6. NavMesh pipeline должен работать в single-thread budget; многопоточность в WebGL считается только опциональным ускорением
|
||||||
|
|
||||||
|
Решение:
|
||||||
|
- архитектура runtime NavMesh не должна зависеть от наличия потоков
|
||||||
|
- базовый режим должен укладываться в бюджет кадра на одном потоке
|
||||||
|
- если deployment позже подтвердит поддержку `SharedArrayBuffer` и `COOP/COEP`, можно добавить threaded optimization, но не делать ее обязательной
|
||||||
|
|
||||||
|
Почему выбрано:
|
||||||
|
- WebGL-host остается одной из целевых платформ
|
||||||
|
- WebGL multithreading требует специальных заголовков и эксплуатационной дисциплины на стороне хостинга
|
||||||
|
- завязка critical gameplay pipeline на эту инфраструктуру слишком рискованна для MVP
|
||||||
|
|
||||||
|
Почему не выбран threaded-only pipeline:
|
||||||
|
- он может работать в editor/desktop и развалиться в реальном WebGL deployment
|
||||||
|
- создаст ложное ощущение приемлемого бюджета, которого не будет на production-hosting
|
||||||
|
|
||||||
|
Последствия:
|
||||||
|
- rebuild должен быть incremental, throttled и bounded
|
||||||
|
- полносценовый bake вокруг камеры не подходит как каноническая модель
|
||||||
|
|
||||||
|
### 7. Первая итерация NavMesh покрывает область вокруг player actor, но долгосрочный контракт расширяется до players + active NPC
|
||||||
|
|
||||||
|
Решение:
|
||||||
|
- для первой проверки гипотезы build priority привязывается к player actor
|
||||||
|
- целевой контракт для multiplayer host: nav coverage должна учитывать игроков и активных NPC
|
||||||
|
|
||||||
|
Почему выбрано:
|
||||||
|
- это минимальный объем для MVP-проверки без ранней переплаты за сложную interest model
|
||||||
|
- при этом заранее фиксируется, что player-only coverage не является конечной архитектурой
|
||||||
|
|
||||||
|
Почему не выбран camera-driven center:
|
||||||
|
- камера не является каноническим gameplay actor
|
||||||
|
- в multiplayer и especially on host камера может не совпадать с зоной активной симуляции
|
||||||
|
- привязка к `Camera.main` ломает переносимость решения из test scene в сетевую сессию
|
||||||
|
|
||||||
|
Последствия:
|
||||||
|
- в коде нельзя оставлять `Camera.main` как канонический источник world/nav interest
|
||||||
|
- target должен представлять actor-level interest, а не presentation-level camera
|
||||||
|
|
||||||
|
### 8. Для MVP поддерживается один тип NavMesh agent
|
||||||
|
|
||||||
|
Решение:
|
||||||
|
- сейчас поддерживается только один `agentTypeID`
|
||||||
|
|
||||||
|
Почему выбрано:
|
||||||
|
- проект на стадии hypothesis/MVP
|
||||||
|
- это уменьшает стоимость runtime bake и настройки AI Navigation
|
||||||
|
- не раздувает матрицу тестирования до появления реальной необходимости
|
||||||
|
|
||||||
|
Почему не выбран multi-agent bake сразу:
|
||||||
|
- рост CPU и memory costs
|
||||||
|
- усложнение отладки при почти нулевой текущей пользе
|
||||||
|
|
||||||
|
Последствия:
|
||||||
|
- при появлении разных классов существ нужно отдельно пересмотреть agent taxonomy
|
||||||
|
|
||||||
|
### 9. В этой фазе решение остается scene-local и не привязывается к VContainer или Addressables
|
||||||
|
|
||||||
|
Решение:
|
||||||
|
- runtime NavMesh реализуется как часть текущего scene-local world runtime
|
||||||
|
- VContainer и Addressables в этой задаче не вводятся
|
||||||
|
|
||||||
|
Почему выбрано:
|
||||||
|
- в проекте пока нет production-ready composition root поверх gameplay world
|
||||||
|
- принудительное добавление DI boundary сейчас даст больше шума, чем пользы
|
||||||
|
- Addressables не подключены и не требуются для гипотезы NavMesh generation
|
||||||
|
|
||||||
|
Почему не выбран ранний DI/bootstrap refactor:
|
||||||
|
- это отвлекает от основной гипотезы по производительности и корректности NavMesh
|
||||||
|
- возникает преждевременная архитектурная сложность при еще нестабильных правилах мира
|
||||||
|
|
||||||
|
Последствия:
|
||||||
|
- код должен оставаться достаточно изолированным, чтобы позже его можно было вынести в runtime service
|
||||||
|
|
||||||
|
## Long-Term Risks
|
||||||
|
|
||||||
|
### Critical
|
||||||
|
|
||||||
|
- WebGL-host может не выдержать одновременно world streaming, runtime NavMesh rebuild и server-authoritative NPC AI.
|
||||||
|
- Любой drift по `seed`, `VoxelWorldConfig` или world rules между peers приведет к расхождению геометрии и локального NavMesh.
|
||||||
|
|
||||||
|
### High
|
||||||
|
|
||||||
|
- Цель в `50` активных NPC может упереться не в один subsystem, а в суммарный CPU budget хоста.
|
||||||
|
- Будущие изменения геометрии потребуют точной invalidation strategy по nav regions; без нее rebuild cost быстро выйдет из-под контроля.
|
||||||
|
- Если client movement в будущем начнет опираться на локальный NavMesh как на authority source, появятся расхождения с host simulation.
|
||||||
|
|
||||||
|
### Medium
|
||||||
|
|
||||||
|
- Late join требует не только `seed/config`, но и корректного воспроизведения authoritative world deltas.
|
||||||
|
- Если region size выбрать слишком крупным, rebuild будет дорогим; если слишком мелким, возрастет число build operations и seam-risk на границах.
|
||||||
|
|
||||||
|
## Downstream Implications
|
||||||
|
|
||||||
|
- `TASK-0001`: этот документ закрывает часть канонических MVP-решений по world/authority/navmesh.
|
||||||
|
- `TASK-0002`: session handshake должен включать world seed, config/version и protocol compatibility checks.
|
||||||
|
- `TASK-0012`: enemy AI проектируется только как host-authoritative.
|
||||||
|
- `TASK-0023`: runtime NavMesh обязан быть local-build, throttled и без camera-driven assumptions.
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
# TASK-0023 Runtime NavMesh Implementation Plan
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Реализовать runtime NavMesh для procedural voxel world без фризов и без camera-driven assumptions, с архитектурой, совместимой с будущей peer-host multiplayer моделью.
|
||||||
|
|
||||||
|
## Inputs And Assumptions
|
||||||
|
|
||||||
|
- текущая test scene: `Assets/Features/VoxelWorld/Scenes/VoxelWorldTestScene.unity`
|
||||||
|
- основной runtime: `Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs`
|
||||||
|
- текущий world config asset:
|
||||||
|
- `chunkSize = 16`
|
||||||
|
- `generationRadius = 3`
|
||||||
|
- `maxMountainHeight = 6`
|
||||||
|
- `renderRegionSizeInChunks = 4`
|
||||||
|
- `maxAsyncChunkJobs = 2`
|
||||||
|
- `maxChunkBuildsPerFrame = 1`
|
||||||
|
- `maxChunkMeshBuildsPerFrame = 1`
|
||||||
|
- `maxColliderAppliesPerFrame = 1`
|
||||||
|
- первая итерация учитывает область вокруг player actor
|
||||||
|
- долгосрочный контракт остается `players + active NPC`
|
||||||
|
- один тип агента
|
||||||
|
- динамические изменения мира пока не реализуются, но точки расширения под них должны быть предусмотрены
|
||||||
|
|
||||||
|
## Chosen Technical Direction
|
||||||
|
|
||||||
|
### 1. Не использовать `NavMeshSurfaceVolumeUpdater` как основу решения
|
||||||
|
|
||||||
|
Причина:
|
||||||
|
- пример из samples двигает один build volume за tracked agent и не подходит как production-модель для chunk streaming
|
||||||
|
- он делает слишком coarse-grained rebuild и оставляет мало контроля над budget, dirty queue и multi-region state
|
||||||
|
|
||||||
|
### 2. Использовать ручной runtime pipeline через `NavMeshBuilder.UpdateNavMeshDataAsync`
|
||||||
|
|
||||||
|
Причина:
|
||||||
|
- дает прямой контроль над build sources, bounds, lifecycle `NavMeshData` и количеством одновременных rebuild
|
||||||
|
- позволяет отказаться от scene-wide source collection и собирать только известные chunk sources
|
||||||
|
- лучше подходит для throttling под WebGL-host
|
||||||
|
|
||||||
|
### 3. Строить NavMesh не per-chunk, а по небольшим nav regions
|
||||||
|
|
||||||
|
Выбор:
|
||||||
|
- отдельный `NavMeshData` на nav region
|
||||||
|
- стартовый размер region рекомендуется сделать `2x2` чанка, configurable отдельно от render regions
|
||||||
|
|
||||||
|
Почему выбран region-based подход:
|
||||||
|
- per-chunk rebuild создает слишком много мелких операций и лишние seam-риски на границах
|
||||||
|
- один большой sliding volume вокруг interest target слишком дорог для WebGL-host
|
||||||
|
- небольшой region дает контролируемый компромисс между стоимостью rebuild и связностью навигации
|
||||||
|
|
||||||
|
### 4. Источник build sources брать из runtime collider-геометрии чанков
|
||||||
|
|
||||||
|
Выбор:
|
||||||
|
- `GroundCollider` каждого чанка дает box source
|
||||||
|
- `MountainCollider.sharedMesh` дает mesh source
|
||||||
|
|
||||||
|
Почему так:
|
||||||
|
- не нужно сканировать всю сцену
|
||||||
|
- не нужно строить отдельную nav-only геометрию на первом этапе
|
||||||
|
- collider topology уже является ближайшим к gameplay физическим представлением поверхности
|
||||||
|
|
||||||
|
### 5. Rebuild делать через dirty queue и budgeted scheduler
|
||||||
|
|
||||||
|
Выбор:
|
||||||
|
- region помечается dirty при `ApplyColliderMesh` и при unload чанка
|
||||||
|
- scheduler сортирует dirty regions по расстоянию до interest actor
|
||||||
|
- одновременно идет максимум один nav rebuild
|
||||||
|
- если region снова стал dirty во время build, версия region увеличивается и после завершения запускается новый rebuild только для актуальной версии
|
||||||
|
|
||||||
|
Почему так:
|
||||||
|
- это bounded и предсказуемо для WebGL-host
|
||||||
|
- исключает лавинообразные rebuild при быстром перемещении игрока
|
||||||
|
|
||||||
|
## Proposed Runtime Structure
|
||||||
|
|
||||||
|
### File Placement
|
||||||
|
|
||||||
|
- расширить `VoxelWorldGenerator` новыми partial-файлами, а не вводить отдельный service layer на этой стадии
|
||||||
|
- рекомендуемые файлы:
|
||||||
|
- `Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.NavMesh.cs`
|
||||||
|
- `Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.NavMesh.Types.cs`
|
||||||
|
|
||||||
|
Причина:
|
||||||
|
- nav lifecycle напрямую зависит от chunk lifecycle, которым уже владеет `VoxelWorldGenerator`
|
||||||
|
- это минимальное изменение без раннего DI/refactor
|
||||||
|
|
||||||
|
### New Runtime Data
|
||||||
|
|
||||||
|
- `NavRegionRuntime`
|
||||||
|
- `NavMeshData NavMeshData`
|
||||||
|
- `NavMeshDataInstance Instance`
|
||||||
|
- `AsyncOperation ActiveBuild`
|
||||||
|
- `int Version`
|
||||||
|
- `bool IsDirty`
|
||||||
|
- `bool BuildRequestedWhileRunning`
|
||||||
|
- `Bounds BuildBounds`
|
||||||
|
- `List<NavMeshBuildSource>` reusable sources buffer
|
||||||
|
- `Queue<Vector2Int> dirtyNavRegions`
|
||||||
|
- `HashSet<Vector2Int> queuedNavRegions`
|
||||||
|
- `Dictionary<Vector2Int, NavRegionRuntime> navRegions`
|
||||||
|
|
||||||
|
### New Config Settings
|
||||||
|
|
||||||
|
Добавить в `VoxelWorldConfig` отдельную секцию `NavMesh`:
|
||||||
|
- `int navRegionSizeInChunks = 2`
|
||||||
|
- `int maxNavMeshBuildsPerFrame = 1`
|
||||||
|
- `int maxConcurrentNavMeshBuilds = 1`
|
||||||
|
- `float navBoundsVerticalPadding`
|
||||||
|
- `float navBoundsHorizontalPadding`
|
||||||
|
- `int navWarmupRadiusInRegions`
|
||||||
|
|
||||||
|
Примечание:
|
||||||
|
- `maxConcurrentNavMeshBuilds` для первой итерации должен остаться `1`
|
||||||
|
- horizontal padding нужен для корректной стыковки границ region data
|
||||||
|
|
||||||
|
## Integration With World Lifecycle
|
||||||
|
|
||||||
|
### Chunk load / update flow
|
||||||
|
|
||||||
|
1. `GenerateChunkData` завершает данные чанка.
|
||||||
|
2. `RenderChunk` собирает render snapshot и collider mesh.
|
||||||
|
3. После фактического применения collider mesh chunk помечает свой nav region dirty.
|
||||||
|
4. Если чанк лежит у границы nav region, дополнительно dirty-mark соседний region, который делит с ним границу.
|
||||||
|
5. Scheduler позже запускает rebuild региона по budget.
|
||||||
|
|
||||||
|
### Chunk unload flow
|
||||||
|
|
||||||
|
1. Перед `runtime.Dispose()` определить nav region чанка.
|
||||||
|
2. Пометить соответствующий region dirty.
|
||||||
|
3. Если region стал пустым и вышел из active nav range, удалить его `NavMeshDataInstance`.
|
||||||
|
|
||||||
|
### Interest target flow
|
||||||
|
|
||||||
|
1. Убрать каноническую зависимость от `Camera.main` как источника стриминга/nav interest.
|
||||||
|
2. Ввести actor-level target semantics.
|
||||||
|
3. Для сохранения сцены использовать rename с `FormerlySerializedAs`, если будет меняться имя поля.
|
||||||
|
4. Для первой итерации target задается явно со сцены или от будущего player actor bootstrap.
|
||||||
|
|
||||||
|
## Region Build Flow
|
||||||
|
|
||||||
|
1. Определить `regionCoord` по координате чанка.
|
||||||
|
2. Вычислить `Bounds` региона с padding по XZ и по высоте.
|
||||||
|
3. Собрать build sources только из чанков, попадающих в region и в соседний margin вокруг него.
|
||||||
|
4. Для каждого активного чанка добавить:
|
||||||
|
- `NavMeshBuildSourceShape.Box` из `GroundCollider`
|
||||||
|
- `NavMeshBuildSourceShape.Mesh` из `MountainCollider.sharedMesh`, если mesh не пустой
|
||||||
|
5. Запустить `NavMeshBuilder.UpdateNavMeshDataAsync` для region-local `NavMeshData`.
|
||||||
|
6. При завершении проверить актуальность версии и либо оставить data, либо сразу перезапустить rebuild актуальной версии.
|
||||||
|
|
||||||
|
## Region Granularity And Boundary Rules
|
||||||
|
|
||||||
|
### Start choice
|
||||||
|
|
||||||
|
- `navRegionSizeInChunks = 2`
|
||||||
|
|
||||||
|
Почему не `1`:
|
||||||
|
- слишком много мелких `NavMeshData`
|
||||||
|
- больше seam pressure на стыках
|
||||||
|
- выше scheduler overhead
|
||||||
|
|
||||||
|
Почему не `4`:
|
||||||
|
- rebuild слишком дорогой для частого runtime update на WebGL-host
|
||||||
|
- это уже заметный кусок от всего active world при `generationRadius = 3`
|
||||||
|
|
||||||
|
### Boundary handling
|
||||||
|
|
||||||
|
- build bounds должны быть больше чистого region rectangle
|
||||||
|
- source collection должна захватывать соседние чанки на один region-margin
|
||||||
|
- region dirty-mark должен учитывать chunk changes на границах
|
||||||
|
|
||||||
|
Причина:
|
||||||
|
- без overlap на границах легко получить cracks и непредсказуемую связность между соседними `NavMeshData`
|
||||||
|
|
||||||
|
## Multiplayer And Authority Contract For This Task
|
||||||
|
|
||||||
|
- базовый voxel world генерируется локально у каждого peer из одинакового deterministic input
|
||||||
|
- NavMesh строится локально у каждого peer и не реплицируется по сети
|
||||||
|
- authoritative gameplay использует host-side NPC simulation
|
||||||
|
- текущая итерация NavMesh coverage вокруг player actor считается временным MVP simplification
|
||||||
|
- при переходе к реальной multiplayer-сцене host должен строить priority coverage вокруг `players + active NPC`
|
||||||
|
- будущие world changes должны приходить как authoritative deltas и маркировать nav regions dirty локально на каждом peer
|
||||||
|
|
||||||
|
## Performance Rules
|
||||||
|
|
||||||
|
- не делать full-scene bake
|
||||||
|
- не пересобирать NavMesh синхронно через `BuildNavMesh()` на gameplay path
|
||||||
|
- не сканировать произвольные scene objects через generic collection APIs, если можно собрать sources из известных chunk runtimes
|
||||||
|
- держать максимум один активный build
|
||||||
|
- переиспользовать buffers, где это возможно
|
||||||
|
- rebuild запускать только после фактического применения collider mesh
|
||||||
|
- unload и load чанков должны только маркировать region dirty, а не запускать немедленный build вне scheduler
|
||||||
|
|
||||||
|
## Verification Plan
|
||||||
|
|
||||||
|
### Manual verification
|
||||||
|
|
||||||
|
1. Запустить `VoxelWorldTestScene`.
|
||||||
|
2. Использовать debug `NavMeshAgent` из AI Navigation samples.
|
||||||
|
3. Проверить, что агент строит путь по поверхности уже загруженных чанков.
|
||||||
|
4. Быстро перемещать actor target по миру и отслеживать отсутствие заметных фризов.
|
||||||
|
5. Проверить unload чанков: после ухода области старый NavMesh не должен оставлять висячие walkable islands в уже удаленных регионах.
|
||||||
|
|
||||||
|
### Debug instrumentation
|
||||||
|
|
||||||
|
- gizmos для region bounds и состояния region build
|
||||||
|
- лог счетчиков:
|
||||||
|
- active nav regions
|
||||||
|
- dirty nav regions
|
||||||
|
- builds started/completed/cancelled as stale
|
||||||
|
|
||||||
|
## Explicit Non-Goals For This Iteration
|
||||||
|
|
||||||
|
- NavMeshObstacle carving
|
||||||
|
- multi-agent bake
|
||||||
|
- DI integration через VContainer
|
||||||
|
- Addressables integration
|
||||||
|
- ownership migration для чанков или NPC
|
||||||
|
- финальная multiplayer interest model вокруг всех actors
|
||||||
|
|
||||||
|
## Execution Order
|
||||||
|
|
||||||
|
1. Добавить nav settings в `VoxelWorldConfig` и resolved settings.
|
||||||
|
2. Добавить runtime структуры nav regions и dirty scheduler в `VoxelWorldGenerator`.
|
||||||
|
3. Привязать dirty-marking к chunk collider apply и unload.
|
||||||
|
4. Реализовать source collection из chunk colliders.
|
||||||
|
5. Реализовать region-local `NavMeshData` lifecycle и async rebuild.
|
||||||
|
6. Убрать camera-driven fallback из world/nav interest path.
|
||||||
|
7. Добавить debug visualization и ручную проверку через sample agent.
|
||||||
|
8. Задокументировать фактические perf observations после первой проверки гипотезы.
|
||||||
@@ -6,12 +6,14 @@ priority: Highest
|
|||||||
area: ai
|
area: ai
|
||||||
owner: abysscion
|
owner: abysscion
|
||||||
created: 2026-03-31
|
created: 2026-03-31
|
||||||
updated: 2026-04-07
|
updated: 2026-04-08
|
||||||
execution_time: 2d
|
execution_time: 2d
|
||||||
depends_on:
|
depends_on:
|
||||||
- TASK-0003
|
- TASK-0003
|
||||||
canonical_docs:
|
canonical_docs:
|
||||||
- docs/tasks/Index.md
|
- docs/tasks/Index.md
|
||||||
|
- docs/architecture/mvp-world-authority-navmesh.md
|
||||||
|
- docs/plans/TASK-0023-runtime-navmesh-implementation-plan.md
|
||||||
related_files:
|
related_files:
|
||||||
- Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs
|
- Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs
|
||||||
---
|
---
|
||||||
@@ -94,4 +96,4 @@ AI врагов (`TASK-0012`) опирается на NavMesh. Воксельн
|
|||||||
|
|
||||||
## Handoff Notes
|
## Handoff Notes
|
||||||
|
|
||||||
Если в проекте нет пакета NavMeshComponents, возможно придется добавить его или реализовать минимальный runtime builder.
|
Если в проекте нет пакета NavMeshComponents, возможно придется добавить его или реализовать минимальный runtime builder.
|
||||||
|
|||||||
Reference in New Issue
Block a user