410 lines
29 KiB
Markdown
410 lines
29 KiB
Markdown
# Клиент: сцены, 3D и анимации — план доработок
|
||
|
||
**Дата:** 2026-10-30
|
||
**Анализ:** DeepSeek выполнен; код, сцены, префабы, SO и конфиги не менялись.
|
||
**Решения:** зафиксированы 2026-10-30 (раздел 8).
|
||
**Статус:** реализация не начата.
|
||
**Связанные документы:** `docs/client-visual-direction.md`,
|
||
`docs/3d-ux-backlog.md`, `docs/Development/ANIMATION-INVENTORY.md`,
|
||
`docs/Development/IMPLEMENTATION_PLAN.md` (TASK-2026-001…018).
|
||
|
||
Документ — приоритизированный бэклог по трём направлениям: разбиение на сцены,
|
||
3D-наполнение каждой сцены, оптимизация анимаций. Он не заменяет
|
||
`IMPLEMENTATION_PLAN.md`: при старте работ пункты переносятся туда отдельными
|
||
TASK с полями «Реализация» и «Проверка» по `AGENT-WORKFLOW.md`.
|
||
|
||
Первый блок (A1 + C1–C3) оформлен как `TASK-2026-019` в
|
||
`docs/Development/IMPLEMENTATION_PLAN.md`; код ещё не менялся.
|
||
|
||
## 0. Границы
|
||
|
||
- без новых пакетов: DOTween/UniTask запрещены (решение 2026-10-24);
|
||
- правила, сервер, протокол и YAML-контент не меняются;
|
||
- `CardMoveAnimator` остаётся единственным планировщиком перемещений карт;
|
||
- сцены и префабы меняются только в рамках конкретного пункта, не попутно;
|
||
- геймплей вручную проверяет пользователь.
|
||
|
||
## 1. Текущее состояние (факты)
|
||
|
||
### 1.1 Сцены
|
||
|
||
| Сцена | В `.unity` | Создаётся кодом | 3D-содержимое |
|
||
|---|---|---|---|
|
||
| `Boot` | камера, свет, Global Volume | контроллера и UI нет; `VanguardApp` из `RuntimeInitializeOnLoadMethod` | нет |
|
||
| `Login` | контроллер, камера, свет | Canvas + `LoginView`, `SceneEnvironment` | пол, 14 кубов (колонны, неон, задник, стол, 4 карты, 2 панели), 2 point light |
|
||
| `Lobby` | контроллер, камера, свет | Canvas + `MetaView`/очередь, `SceneEnvironment` | пол, 2 колонны, 2 стола, стена, световая полоса |
|
||
| `DeckBuilder` | контроллер, камера, свет | Canvas + `DeckBuilderView`, `SceneEnvironment` | пол, 3 куба |
|
||
| `Battle` | контроллер, камера, свет | Canvas + `BattleView`, `SceneEnvironment`, `GameBoard3D` | 4 фоновых куба + процедурное поле |
|
||
| `GameBoard3D` | камера/свет из конфига, `PlaymatSandbox` | песочница поля без сервера | поле целиком |
|
||
|
||
Переходы: `SceneFlow.LoadAsync` → `LoadSceneAsync(Single)`, ожидание `isDone`,
|
||
без fade, прогресса и `allowSceneActivation` (`SceneFlow.cs:63-80`).
|
||
`GameBoard3D.unity` отсутствует в Build Settings; константа `PlaymatSandbox`
|
||
метода загрузки не имеет.
|
||
|
||
### 1.2 3D и ассеты
|
||
|
||
- Окружение одно на все сцены: `SceneEnvironment.Build(SceneTheme)` +
|
||
`switch` по теме (`SceneEnvironment.cs:22`, декорации `:110-159`).
|
||
- Камера и свет заданы дважды: `SceneBuilder` пишет их в `.unity`, а
|
||
`SceneEnvironment` перезаписывает в рантайме другими значениями
|
||
(FOV 60 в сцене против 46/52 в рантайме; интенсивность света 0.9 против 1.15).
|
||
- Каждый примитив фона получает собственный `Material` в рантайме
|
||
(`SceneEnvironment.cs:177-202`); ассетов `.mat` в проекте нет.
|
||
- Ассеты: 246 `.jpg` арта карт в `Resources/CardArt`, 0 `.fbx`, 0 `.mat`,
|
||
0 `.shader`, 0 `.anim`, 0 `.controller`, 4 prefab-оверлея.
|
||
- Addressables `4.1.1` установлен, но `Addressables.*` в коде не используется.
|
||
- `AudioListener` есть только в `Boot` (в остальных сценах — 0).
|
||
- Global Volume есть только в `Boot`; в сценах-экранах нет
|
||
`UniversalAdditionalCameraData` — нужна визуальная проверка пост-обработки.
|
||
- `LODGroup`, baked-свет и occlusion отсутствуют; материалы и меши стола
|
||
создаются процедурно (`BoardMaterials`, `BoardMeshLibrary`).
|
||
|
||
### 1.3 Анимации
|
||
|
||
Уже сделано и переделывать не нужно: пул `Card3D`, `sharedMaterial` +
|
||
`MaterialPropertyBlock`, кеши мешей и текстур, единый `Tick` из
|
||
`GameBoard3D.Update`, FPS-независимая интерполяция (экспонента и smoothstep
|
||
на `Time.unscaledDeltaTime`), отсутствие `Update` на каждой карте.
|
||
|
||
Проблемы, подтверждённые кодом:
|
||
|
||
- `UpdateBoostArrows` создаёт `new List<(Vector3, Vector3)>` каждый кадр, а
|
||
`BattleCard` — `Players.FirstOrDefault(lambda)` 2–4 раза за кадр; якоря
|
||
стрелок ищутся линейным перебором `_cards.Values`.
|
||
- Стрелки обновляются дважды за кадр: `_arrowDirector.Tick` и
|
||
`UpdateAttackArrowPoints` (`GameBoard3D.Update`), плюс
|
||
`AttackArrowView.ApplyColor` создаёт массив `new[] { _shaft, _head }`.
|
||
- `UpdateHandView` пересчитывает позы обеих рук каждый кадр и вызывается
|
||
повторно из `Render`, hover и колбэков (`GameBoard3D.cs:183, 511, 607,
|
||
732, 1163, 2230, 2372`) — в одном кадре возможен двойной `SetPose`.
|
||
- Строки в кадре: таймер и банк (`BattleView.UpdateClock`), `Describe()` у
|
||
`InputLockService` при активном локе, `LocalizedText` для каждого лейбла.
|
||
- Hover/drag используют `Physics.RaycastAll` с сортировкой через лямбду и
|
||
`GetComponentInParent` на каждый хит.
|
||
- `AnimationConfig` содержит ~15 полей, которые код не читает
|
||
(`ANIMATION-INVENTORY.md`, раздел I): риск неверной настройки.
|
||
|
||
## 2. Сводка приоритетов
|
||
|
||
| ID | Что | Приоритет | Зачем |
|
||
|---|---|---|---|
|
||
| A1 | Загрузочная сцена и fade | P0 | убрать вспышку при смене сцены |
|
||
| A2 | Песочница в Build Settings | P0 | `RunPlaymat` и песочница в билде |
|
||
| A3 | Один владелец камеры/света | P0 | убрать рассинхрон `.unity` и рантайма |
|
||
| A4 | `SceneProfile` на сцену | P1 | настройка сцены без правки кода |
|
||
| A5 | AudioListener на персистентном объекте | P1 | звук во всех сценах |
|
||
| A6 | Пост-обработка в сценах-экранах | P1 | одинаковый look |
|
||
| A7 | Additive-слой общих сервисов | P2 | не пересоздавать Canvas |
|
||
| A8 | SceneSmoke для DeckBuilder/Battle | P2 | покрытие смоуком |
|
||
| B1 | Общие материалы вместо `new Material` | P0 | батчинг, редактируемость |
|
||
| B2 | Login: полноценная 3D-сцена | P0 | первый экран |
|
||
| B3 | Lobby и DeckBuilder: 3D-сцены | P1 | сейчас 6 и 3 куба |
|
||
| B4 | Battle: окружение вокруг поля | P1 | задник/свет без правки поля |
|
||
| B5 | Prefab-экраны UI | P1 | уйти от code-generated |
|
||
| B6 | Addressables для арта | P1 | разгрузить `Resources` |
|
||
| B7 | Light/reflection probes, baked-свет | P2 | качество света |
|
||
| B8 | Живое окружение | P2 | неон, частицы, скайбокс |
|
||
| C1 | Аллокации в стрелках | P0 | 0 B/кадр в бою |
|
||
| C2 | Один тик руки | P0 | убрать двойной `SetPose` |
|
||
| C3 | Строки не каждый кадр | P0 | GC и цена UI |
|
||
| C4 | Событийный `InputLock` | P1 | паузы по факту, не по таймеру |
|
||
| C5 | Raycast без аллокаций | P1 | hover/drag |
|
||
| C6 | Синхронизировать `AnimationConfig` | P1 | убрать мёртвые поля |
|
||
| C7 | Профилирование и бюджет кадра | P2 | 60 fps, метрики |
|
||
| C8 | Анимационный LOD | P2 | не тикать невидимое |
|
||
|
||
## 3. A — Разбиение на сцены
|
||
|
||
- [ ] **A1. P0 — Загрузочная сцена и плавный переход.**
|
||
Файлы: `Runtime/App/SceneFlow.cs`, новый `Runtime/UI/LoadingOverlay.cs`,
|
||
`Runtime/App/VanguardApp.cs`, `Scenes/Boot.unity`.
|
||
Сделать: `Boot` становится загрузочной сценой — прогревает `VanguardApp` и
|
||
сервисы, показывает прогресс `operation.progress` и fade 0.15–0.25 с, затем
|
||
грузит `Login`; для переходов между экранами используется тот же overlay.
|
||
Зачем: переход идёт без fade и прогресса — сцена меняется одним кадром,
|
||
экран загрузки не показывается.
|
||
Критерий: Login → Lobby → Battle без вспышки; прогресс виден на первом
|
||
входе в Lobby.
|
||
Зависит: нет.
|
||
|
||
- [ ] **A2. P0 — Песочница поля в сборке.**
|
||
Файлы: `ProjectSettings/EditorBuildSettings.asset` (через
|
||
`Editor/SceneBuilder.cs`), `Runtime/App/SceneFlow.cs`.
|
||
Сделать: добавить `Assets/Vanguard/Scenes/GameBoard3D.unity` после `Battle`,
|
||
метод `LoadPlaymatAsync`, доступ только в dev-сборке.
|
||
Зачем: константа `PlaymatSandbox` не имеет загрузчика, сцены нет в билде.
|
||
Критерий: песочница открывается в standalone, `SceneSmoke.RunPlaymat`
|
||
проходит.
|
||
Зависит: A1.
|
||
|
||
- [ ] **A3. P0 — Один владелец камеры и света.**
|
||
Файлы: `Editor/SceneBuilder.cs`, `Runtime/App/SceneEnvironment.cs`,
|
||
`*.unity`.
|
||
Сделать: выбрать одну модель — сцены хранят камеру/свет, а
|
||
`SceneEnvironment` их не перезаписывает, либо сцены пустые и всё создаёт
|
||
окружение. Зафиксировать в `client-visual-direction.md`.
|
||
Зачем: сейчас FOV 60 в сцене против 46/52 в рантайме, свет 0.9 против 1.15;
|
||
правки в сцене не действуют.
|
||
Критерий: значения в инспекторе совпадают с рантаймом;
|
||
`SceneBuilder.BuildAll` не создаёт расхождений.
|
||
Зависит: нет (A4 опирается на это решение).
|
||
|
||
- [ ] **A4. P1 — `SceneProfile` (ScriptableObject) на сцену.**
|
||
Файлы: новый `Runtime/App/SceneProfile.cs` + 4 ассета,
|
||
`Runtime/App/SceneEnvironment.cs`, контроллеры сцен.
|
||
Сделать: вынести камеру, свет, палитру, туман, декор и пост в профиль;
|
||
`Build(SceneProfile)` вместо `switch(SceneTheme)`.
|
||
Зачем: настройка сцены сейчас требует правки кода; поля
|
||
`ScenePalette.Zone/EnemyZone` не читаются.
|
||
Критерий: внешний вид сцены меняется правкой ассета, без компиляции.
|
||
Зависит: A3.
|
||
|
||
- [ ] **A5. P1 — AudioListener и аудио-корень.**
|
||
Файлы: `Runtime/App/VanguardApp.cs`, `*.unity`.
|
||
Сделать: один `AudioListener` на персистентном объекте, убрать дубль из
|
||
`Boot`-камеры.
|
||
Зачем: единственный listener в `Boot` уничтожается при Single-загрузке —
|
||
при добавлении звука его не будет слышно.
|
||
Критерий: ровно один `AudioListener` в любой сцене, звук слышен.
|
||
Зависит: нет.
|
||
|
||
- [ ] **A6. P1 — Пост-обработка в сценах-экранах.**
|
||
Файлы: `*.unity`, `Assets/Settings/SampleSceneProfile.asset`.
|
||
Сделать: `UniversalAdditionalCameraData` и Volume (или общий профиль через
|
||
Global Settings) на всех экранах; сверить bloom/vignette/tonemapping.
|
||
Зачем: Global Volume есть только в `Boot`, в остальных сценах
|
||
пост-обработка, вероятно, не применяется.
|
||
Критерий: одинаковый look `Boot`/`Login`/`Lobby`/`DeckBuilder`/`Battle`.
|
||
Зависит: A3.
|
||
|
||
- [ ] **A7. P2 — Additive-слой общих объектов.**
|
||
Сделать: Canvas оверлеев, EventSystem и аудио грузить один раз и не
|
||
пересоздавать при смене экрана.
|
||
Зачем: каждый экран заново строит Canvas через `UiKit.CreateCanvas`.
|
||
Критерий: Lobby ↔ DeckBuilder без пересоздания оверлеев и мигания.
|
||
Зависит: A1.
|
||
|
||
- [ ] **A8. P2 — Расширить `SceneSmoke`.**
|
||
Файлы: `Editor/SceneSmoke.cs`.
|
||
Сделать: проверять `DeckBuilder` и `Battle` (контроллер, окружение, камера,
|
||
`AudioListener`), сверять FOV/интенсивность света с профилем.
|
||
Зачем: сейчас проверяются только `Login`/`Lobby`.
|
||
Критерий: смоук падает при потере окружения или listener в любой сцене.
|
||
Зависит: A3–A6.
|
||
|
||
## 4. B — 3D в каждой сцене
|
||
|
||
- [ ] **B1. P0 — Общие материалы вместо `new Material`.**
|
||
Файлы: `Runtime/App/SceneEnvironment.cs`, новый `Assets/Vanguard/Art/…`.
|
||
Сделать: 3–5 общих URP-материалов ассетами; фон и декор используют их.
|
||
Зачем: сейчас каждый примитив получает инстанс материала в рантайме
|
||
(`:177-202`), это ломает батчинг и не редактируется в инспекторе.
|
||
Критерий: Frame Debugger без лишних `SetPass`, SRP Batcher активен.
|
||
Зависит: A4.
|
||
|
||
- [ ] **B2. P0 — Login: полноценная 3D-сцена.**
|
||
Сделать: authored-стол, 4–6 карт, неоновые направляющие, задник; свет и
|
||
камера из профиля; геометрия — префабы, а не код.
|
||
Зачем: первый экран сейчас собран из 14 кубов в рантайме.
|
||
Критерий: визуально читаемый стол; в рантайме не создаётся статическая
|
||
геометрия.
|
||
Зависит: B1, A4, референс экрана от пользователя.
|
||
|
||
- [ ] **B3. P1 — Lobby и DeckBuilder: 3D-сцены.**
|
||
Сделать: Lobby — зал с постаментом/витриной и световыми акцентами;
|
||
DeckBuilder — стол с матом, стопками и подсветкой зон.
|
||
Зачем: сейчас 6 и 3 куба, экраны визуально пустые.
|
||
Критерий: у каждой сцены своя композиция, а не общий `switch` с кубами.
|
||
Зависит: B1, A4, референс экрана от пользователя.
|
||
|
||
- [ ] **B4. P1 — Battle: окружение вокруг поля.**
|
||
Сделать: задник, постамент, световые акценты вокруг `GameBoard3D`, не
|
||
вмешиваясь в геометрию поля и `PlaymatLayout`.
|
||
Зачем: сейчас 4 фоновых куба; поле выглядит висящим в пустоте.
|
||
Критерий: поле читается как стол в помещении; `GameBoard3DTests` без
|
||
изменений.
|
||
Зависит: B1, A4, референс боевого экрана от пользователя.
|
||
|
||
- [ ] **B5. P1 — Prefab-экраны UI.**
|
||
Сделать: перенести code-generated панели в префабы (по одному экрану за
|
||
шаг), `UiKit` оставить фабрикой для мелких элементов.
|
||
Зачем: `client-visual-direction.md` фиксирует это как следующий шаг;
|
||
сейчас UI собирается кодом и не редактируется дизайнером.
|
||
Критерий: экран собирается из префаба, поведение и тесты не меняются.
|
||
Зависит: приёмка TASK-2026-003 (HUD), иначе конфликт правок.
|
||
|
||
- [ ] **B6. P1 — Addressables для арта.**
|
||
Сделать: инициализировать Addressables-группы, перенести арт карт из
|
||
`Resources/CardArt` в адресуемые группы, грузить по `cardId`
|
||
(`CardImageService`), оставить процедурный fallback по клану.
|
||
Зачем: 246 `.jpg` лежат в `Resources` и грузятся целиком; пакет установлен,
|
||
но не используется.
|
||
Критерий: арт грузится адресуемо и выгружается, `Resources` не растёт;
|
||
контракт `BoardArt`/`CardArt` не меняется.
|
||
Зависит: нет; модели карт и стола остаются процедурными (решение 8.1).
|
||
|
||
- [ ] **B7. P2 — Light/reflection probes и baked-свет.**
|
||
Сделать: запечь статику сцен, добавить probes для карт и стола.
|
||
Зачем: `m_LightingDataAsset` пустой, освещение только realtime.
|
||
Критерий: статичный свет не считается каждый кадр, карты получают
|
||
корректные отражения.
|
||
Зависит: B2–B4 (финальная геометрия).
|
||
|
||
- [ ] **B8. P2 — Живое окружение.**
|
||
Сделать: анимация неона, лёгкие частицы, скайбокс/градиент, дыхание камеры
|
||
в Login/Lobby.
|
||
Зачем: окружение статично; при этом правила запрещают DOTween — только
|
||
существующий ручной тик.
|
||
Критерий: эффекты не блокируют ввод и не добавляют аллокаций в кадре.
|
||
Зависит: B2–B4, C1–C3.
|
||
|
||
## 5. C — Анимации: плавность и стоимость кадра
|
||
|
||
- [ ] **C1. P0 — Убрать покадровые аллокации в стрелках.**
|
||
Файлы: `Board3D/View/GameBoard3D.cs` (`UpdateBoostArrows`,
|
||
`UpdateAttackArrowPoints`, `BattleCard`), `Animation/ArrowDirector.cs`,
|
||
`View/AttackArrowView.cs`.
|
||
Сделать: кешировать список пар, заменить LINQ на индексированные циклы,
|
||
кешировать якоря на `Render`, обновлять геометрию один раз за кадр, убрать
|
||
`new[] { _shaft, _head }` в `ApplyColor`.
|
||
Зачем: `new List` каждый кадр, 2–4 LINQ-делегата, 4 `UpdateGeometry` на
|
||
стрелку за кадр.
|
||
Критерий: 0 B/кадр в бою без движения (Profiler), визуал не меняется.
|
||
Зависит: нет.
|
||
|
||
- [ ] **C2. P0 — Один тик и один `SetPose` руки.**
|
||
Файлы: `Board3D/View/GameBoard3D.cs` (`UpdateHandView`), `Board3D/View/Card3D.cs`.
|
||
Сделать: dirty-флаг на изменение состава руки, камеры, hover и staged-списка;
|
||
убрать повторный вызов из `Render`/колбэков, если позы уже посчитаны.
|
||
Зачем: позы обеих рук пересчитываются каждый кадр, возможен двойной
|
||
`SetPose` в одном кадре.
|
||
Критерий: при неподвижной камере и руке расчёт не выполняется; hover,
|
||
добор, муллиган и reflow без регрессий.
|
||
Зависит: C1 (общий бюджет кадра).
|
||
|
||
- [ ] **C3. P0 — Строки не каждый кадр.**
|
||
Файлы: `UI/BattleView.cs` (`UpdateClock`), `UI/Localization.cs`
|
||
(`LocalizedText`), `Battle/InputLockService.cs` (`Describe`).
|
||
Сделать: обновлять текст только при изменении значения; кешировать перевод
|
||
до смены языка; убрать `Describe`/LINQ из кадрового пути.
|
||
Зачем: таймер и банк пишутся каждый кадр, `LocalizedText` проходит
|
||
`Loc.Translate` на каждый лейбл, при локе — `string.Join`/LINQ.
|
||
Критерий: в покое нет аллокаций от UI; переключение RU/EN по-прежнему
|
||
мгновенное.
|
||
Зависит: нет.
|
||
|
||
- [ ] **C4. P1 — Событийный `InputLock` вместо таймера.**
|
||
Файлы: `Board3D/Animation/CardMoveAnimator.cs`,
|
||
`Board3D/View/GameBoard3D.cs`, `Battle/InputLockService.cs`.
|
||
Сделать: снимать `board`-лок по завершении очереди и stagger, таймер
|
||
оставить только страховкой.
|
||
Зачем: лок держится `cardMoveDuration * 3 + tail` и не совпадает с фактом
|
||
окончания анимаций — лишние паузы или преждевременный ввод.
|
||
Критерий: ввод открывается сразу после последней карты; зависаний нет
|
||
(`CancelAll`, reconnect, отмена).
|
||
Зависит: C2.
|
||
|
||
- [ ] **C5. P1 — Raycast без аллокаций.**
|
||
Файлы: `Board3D/View/GameBoard3D.cs` (`UpdateHandHover`, drag, targeting).
|
||
Сделать: `RaycastNonAlloc` с переиспользуемым буфером, выбор ближайшего без
|
||
`Array.Sort` с лямбдой, кеш `GetComponentInParent`.
|
||
Зачем: `RaycastAll` + сортировка + `GetComponentInParent` на каждый хит
|
||
каждый кадр при активном указателе.
|
||
Критерий: наведение и drag без аллокаций; выбор карты не изменился.
|
||
Зависит: C1.
|
||
|
||
- [ ] **C6. P1 — Синхронизировать `AnimationConfig` с кодом.**
|
||
Файлы: `Battle/AnimationConfig.cs`, `Resources/AnimationConfig.asset`,
|
||
`docs/Development/ANIMATION-INVENTORY.md`.
|
||
Сделать: удалить или явно пометить поля, которые не читаются или читаются
|
||
не по назначению: `cardFlipDuration`, `callMoveDuration`,
|
||
`cameraRezoomDuration`, `arrowHideDuration`, `retireDuration`,
|
||
`attackDeclare` и паузы feedback — не читаются; `cardMoveDuration` —
|
||
только таймаут board-lock; `arrowFadeDuration` и `boostThickness` дублируют
|
||
`ArrowConfig`. Asset привести к фактическим значениям.
|
||
Зачем: ~15 мёртвых полей — источник неверной настройки.
|
||
Критерий: каждое поле читается кодом или удалено; тест на загрузку ассета.
|
||
Зависит: нет.
|
||
|
||
- [ ] **C7. P2 — Профилирование и бюджет кадра.**
|
||
Сделать: маркеры Profiler в `CardMoveAnimator`, `GameBoard3D.Update`,
|
||
`UpdateHandView`; зафиксировать бюджет (60 fps, доска ≤ 1 мс на референсном
|
||
ПК); выровнять `shadowDistance` (40 в QualitySettings против 50 в URP asset);
|
||
MSAA оставить выключенным (решение 2026-10-30) и проверить края карт с TAA.
|
||
Зачем: сейчас метрик нет, настройки противоречат друг другу.
|
||
Критерий: отчёт «до/после» с числами, зафиксированный бюджет в документе.
|
||
Зависит: C1–C5.
|
||
|
||
- [ ] **C8. P2 — Анимационный LOD.**
|
||
Сделать: не тикать карты вне экрана и стопки, обновлять их по dirty;
|
||
уменьшить частоту обновления рук противника и дальних стопок.
|
||
Зачем: `GameBoard3D.Update` тикает все карты в `_cards` каждый кадр.
|
||
Критерий: то же визуальное поведение, меньше работы в кадре при 40+ картах.
|
||
Зависит: C2, C7.
|
||
|
||
## 6. Порядок работ
|
||
|
||
1. **Старт (P0):** A1 → C1 → C2 → C3 → A2. Загрузочная `Boot` и чистый кадр
|
||
дают сразу видимый эффект и не конфликтуют с приёмкой HUD.
|
||
2. **3D-база (P0):** A3 → A4 → B1 → B6.
|
||
3. **Экраны (P1):** B2 → A5 → A6 → B3 → B4; B2–B4 стартуют после референсов.
|
||
4. **Анимации (P1):** C4 → C5 → C6.
|
||
5. **UI (P1):** B5 — после приёмки TASK-2026-003.
|
||
6. **Полировка (P2):** A7 → A8 → B7 → B8 → C7 → C8.
|
||
|
||
Правило: этапы 1–2 не меняют компоновку HUD из TASK-2026-003, пока идёт его
|
||
ручная приёмка; в `BattleView` допускаются только точечные правки вне
|
||
разметки (например, `UpdateClock` в C3).
|
||
|
||
## 7. Проверки
|
||
|
||
- Клиентские EditMode в копии проекта без `Library` (команда в
|
||
`docs/SESSION-STATE.md`).
|
||
- `SceneSmoke.Run` и `SceneSmoke.RunPlaymat` после каждого блока сцен.
|
||
- `SceneBuilder.BuildAll` — сцены пересобираются без ошибок, `.meta`
|
||
контроллеров на месте.
|
||
- Standalone-сборка на копии: переходы, пост-обработка, звук, песочница.
|
||
- Profiler: Allocation Call Stacks (0 B/кадр в бою без движения), Frame
|
||
Debugger для батчинга.
|
||
- Регрессии: drag&drop, ride/call/move, добор, муллиган, trigger, урон,
|
||
hover руки.
|
||
- Ручная приёмка визуала и геймплея — пользователем.
|
||
|
||
## 8. Решения и риски
|
||
|
||
**Зафиксировано пользователем 2026-10-30:**
|
||
|
||
1. **3D-арт.** Работаем на примитивах с нормальными URP-материалами; закупка
|
||
и генерация моделей в объём не входят. Модели карты/стола можно добавить
|
||
позже без смены контрактов `BoardArt`/`CardArt`.
|
||
2. **`Boot`.** Остаётся первой сценой и становится загрузочной: прогрев
|
||
сервисов, прогресс, fade, затем `Login` (входит в A1).
|
||
3. **Addressables.** Арт переводим сейчас, отдельным шагом (B6), не дожидаясь
|
||
prefab-экранов.
|
||
4. **MSAA.** Остаётся выключенным; края карт сглаживаются TAA. Пересматривать
|
||
только по результатам C7.
|
||
5. **Референсы.** Пользователь готовит мокапы для всех экранов: `Login`,
|
||
`Lobby`, `DeckBuilder` (включая экран колод), `Battle`. B2–B4 стартуют
|
||
после получения соответствующего референса.
|
||
6. **Первый блок.** A1 + C1–C3.
|
||
|
||
**Риски:**
|
||
|
||
- правки сцен ломают GUID-ссылки — сцены пересобираются `SceneBuilder`,
|
||
копировать `.meta` вместе со скриптами;
|
||
- перенос UI в префабы (B5) конфликтует с TASK-2026-003, пока HUD не принят;
|
||
- `new Material` в рантайме нельзя заменить на ассеты, пока не решён A3/A4
|
||
(кто владеет материалами сцены);
|
||
- референсы для `Battle` могут потребовать правок HUD из TASK-2026-003 —
|
||
тогда это отдельная задача, а не попутная правка.
|
||
|
||
## 9. Вне объёма
|
||
|
||
- боевые правила, сервер, протокол, баланс, YAML-контент;
|
||
- HUD по мокапу (TASK-2026-003) и боевые анимации из
|
||
`docs/3d-ux-backlog.md` (cost overlay, attack resolution queue,
|
||
world-space Power/Shield);
|
||
- звук и настройки как отдельная система (кроме A5 — listener);
|
||
- лаунчер WPF и сборка релиза.
|