some changes
This commit is contained in:
@@ -1,243 +1,285 @@
|
||||
# MyGamePlayable — контекст проєкту
|
||||
# MyGamePlayable — Project Instructions
|
||||
|
||||
Playable ad на three.js + пропрієтарний SDK HitPlay. Цей файл вантажиться в
|
||||
кожну сесію — тримай його **інструкцією, а не щоденником**.
|
||||
## Context rules
|
||||
|
||||
This file contains persistent project rules and non-obvious constraints, not history.
|
||||
|
||||
**Keep this file under 200 lines.**
|
||||
|
||||
Add only:
|
||||
|
||||
* rules that affect future implementation decisions;
|
||||
* non-obvious SDK/library/toolchain pitfalls;
|
||||
* structural conventions;
|
||||
* public SDK APIs that are not obvious from code.
|
||||
|
||||
Do not add:
|
||||
|
||||
* development history, dates, or completed tasks;
|
||||
* test/build results;
|
||||
* current tuning values;
|
||||
* information obvious from nearby code;
|
||||
* temporary project state.
|
||||
|
||||
If knowledge is specific to one subsystem, prefer `docs/<topic>.md`.
|
||||
If it explains why a decision was made, put the detailed history in `docs/worklog/`.
|
||||
|
||||
When a pitfall is fixed by a utility, replace the explanation with one short rule
|
||||
pointing to that utility.
|
||||
|
||||
Worklog files:
|
||||
`docs/worklog/NN-short-english-name.md`
|
||||
Update `docs/worklog/README.md` when adding one.
|
||||
|
||||
---
|
||||
|
||||
## ПРАВИЛА ВЕДЕННЯ ЦЬОГО ФАЙЛУ
|
||||
## Stack
|
||||
|
||||
**Стеля — 300 рядків.** Перевищив — перенеси найстаріше й уже усталене в
|
||||
`docs/worklog/`. Історія розробки живе там, не тут.
|
||||
* Three.js `0.185` via `@hitplay/playable_template` + `@hitplay/ads_common`.
|
||||
* Physics: `cannon-es`.
|
||||
* Tweens: `@tweenjs/tween.js` via SDK `TweenC`.
|
||||
* VFX: `three.quarks`, designer `.json` prefabs.
|
||||
* Audio: `howler`.
|
||||
* Input: `nipplejs`.
|
||||
* Debug UI: `lil-gui`.
|
||||
* Analytics: `bytebrew-web-sdk`.
|
||||
* Toolchain: Vite 6 + TypeScript 7.
|
||||
|
||||
### Що ДОДАВАТИ сюди
|
||||
### Runtime rules
|
||||
|
||||
- правило або конвенцію, що змінює майбутні рішення («використовуй утиліту X,
|
||||
не пиши формулу заново»);
|
||||
- граблі, які **вкусять ще раз** (поведінка SDK/бібліотеки/тулчейна, а не
|
||||
разовий баг у власному коді);
|
||||
- зміну структури: новий контролер, нова утиліта, новий конфіг-параметр;
|
||||
- нову команду або публічний API SDK, який не очевидний з коду.
|
||||
|
||||
### Що НЕ додавати
|
||||
|
||||
- «сьогодні зробив…», «Day N», хронологію, цитати з чату;
|
||||
- «перевірено в браузері / білд пройшов» — це не знання, це подія;
|
||||
- підібрані числа (`CAMERA_OFFSET`, тривалості, відсотки) — вони живуть у коді
|
||||
й там же змінюються; сюди — тільки *де* важіль, не його поточне значення;
|
||||
- переказ того, що й так видно з коду за 10 секунд.
|
||||
|
||||
### Куди що писати
|
||||
|
||||
| Знання | Місце |
|
||||
|---|---|
|
||||
| Пояснює один файл/рядок | коментар у коді |
|
||||
| Перетинає кілька файлів або є політикою проєкту | `CLAUDE.md` |
|
||||
| «Чому ми дійшли саме до цього» | `docs/worklog/` |
|
||||
|
||||
### Коли граблі усунені в корені
|
||||
|
||||
Якщо гочтя закрита утилітою — **заміни розповідь одним рядком** з посиланням на
|
||||
утиліту. Приклад: `.chain()`-баг → `src/utils/tweenChain.ts`. Довга версія
|
||||
лишається в `docs/worklog/02-day6-tween-animations.md`.
|
||||
|
||||
### Новий запис у worklog
|
||||
|
||||
`docs/worklog/NN-коротка-англійська-назва.md`, `NN` — наступний номер.
|
||||
Додай рядок у `docs/worklog/README.md`. Один H1 на файл.
|
||||
* `UpdateController.Instance.onUpdate` is the only update event. `delta` is seconds.
|
||||
* SDK calls `world.fixedStep()` every frame via `Physics_internal.update`.
|
||||
Never call `step()` / `fixedStep()` manually.
|
||||
* Physics initialization: `Physics_internal.init(...)` in `beforeResourcesLoadedCb.ts`.
|
||||
* Call `TweenC.init()` exactly once. It is already called in
|
||||
`beforeResourcesLoadedCb.ts`.
|
||||
* Use `ThreeC.removeFromScene(obj)` to remove spawned meshes.
|
||||
|
||||
---
|
||||
|
||||
## Стек
|
||||
## Commands
|
||||
|
||||
- **three.js `0.185`** (НЕ Babylon), обгорнутий у SDK `@hitplay/playable_template`
|
||||
+ `@hitplay/ads_common` (приватні пакети HitPlay).
|
||||
- Фізика: `cannon-es`. **SDK сам кличе `world.fixedStep()` щокадру**
|
||||
(`Physics_internal.update`) — свій `step()`/`fixedStep()` НЕ викликати.
|
||||
Ініт — `Physics_internal.init(...)` у `beforeResourcesLoadedCb.ts`.
|
||||
- Твіни: `@tweenjs/tween.js` через SDK-обгортку `TweenC`. **`TweenC.init()`
|
||||
треба покликати один раз** (є в `beforeResourcesLoadedCb.ts`), інакше
|
||||
`TweenC.add()` тихо нічого не анімує.
|
||||
- VFX: `three.quarks` — дизайнерські префаби з quarks.art (`.json`).
|
||||
- Звук `howler`, ввід `nipplejs`, дебаг-панель `lil-gui`, аналітика `bytebrew-web-sdk`.
|
||||
- Тулчейн: Vite 6 + TypeScript 7.
|
||||
| Command | Purpose |
|
||||
| ---------------- | --------------------------------------------------------- |
|
||||
| `npm start` | Vite dev server |
|
||||
| `npm run export` | Single-file HTML → `export/output_export_browser_en.html` |
|
||||
| `npm run build` | 24ADS platform zip → `zip/` |
|
||||
| `npm run update` | Update HitPlay SDK packages |
|
||||
|
||||
`UpdateController.Instance.onUpdate` — **єдиний** update-івент, `delta` в секундах.
|
||||
`ThreeC.removeFromScene(obj)` — видалення спавнених мешів.
|
||||
Verify changes with `npx vite build`.
|
||||
|
||||
## Команди
|
||||
Use `stats.html` when investigating bundle-size growth.
|
||||
|
||||
| Команда | Що робить |
|
||||
|---|---|
|
||||
| `npm start` | vite dev-сервер |
|
||||
| `npm run export` | single-file HTML → `export/output_export_browser_en.html` |
|
||||
| `npm run build` | zip для платформи 24ADS → `zip/` |
|
||||
| `npm run update` | оновити `@hitplay/ads_common` + `playable_template` |
|
||||
---
|
||||
|
||||
Перевірка змін — `npx vite build` (у проєкті прийнято верифікувати саме білдом).
|
||||
`stats.html` — rollup-visualizer, дивитись коли розпухає бандл.
|
||||
## Structure
|
||||
|
||||
## Структура
|
||||
|
||||
```
|
||||
src/index.ts точка входу, Template.initConfig({...})
|
||||
src/controllers/*C.ts одна система = один файл, суфікс C, плоский список
|
||||
src/configUIParams/ категорії конфігу, які рендерить платформа 24ADS
|
||||
src/templateConfig/*Cb.ts лайфсайкл-колбеки SDK
|
||||
src/utils/ спільні формули (див. нижче)
|
||||
src/resources/ meshes/images/fonts/vfx/sounds
|
||||
src/css/ main.css, ui.css, health-bar.css
|
||||
docs/worklog/ архів історії розробки
|
||||
```text
|
||||
src/index.ts entry point / Template.initConfig()
|
||||
src/controllers/*C.ts one system per file, flat structure
|
||||
src/configUIParams/ 24ADS config categories
|
||||
src/templateConfig/*Cb.ts SDK lifecycle callbacks
|
||||
src/utils/ shared utilities
|
||||
src/resources/ meshes/images/fonts/VFX/sounds
|
||||
src/css/ main.css, ui.css, health-bar.css
|
||||
docs/worklog/ development history
|
||||
```
|
||||
|
||||
Підпапки в `controllers/` **свідомо не зроблені** — SDK сам плаский, вигоди нуль.
|
||||
Do not create controller subdirectories. The SDK uses a flat structure and
|
||||
there is no benefit in mirroring artificial subfolders.
|
||||
|
||||
### Утиліти — використовувати, не переписувати
|
||||
---
|
||||
|
||||
- `perspectiveScale.ts` — компенсація перспективи при польоті до/від камери.
|
||||
- `screenWorldProjection.ts` — world↔screen в обидва боки.
|
||||
- `tweenChain.ts` — коректна реєстрація ланцюжка твінів.
|
||||
- `flashOverlayMaterial.ts` — адитивний флеш-оверлей.
|
||||
## Utilities — reuse, don't reimplement
|
||||
|
||||
### DI-патерн
|
||||
* `perspectiveScale.ts` — perspective compensation for flight toward/away from camera.
|
||||
* `screenWorldProjection.ts` — world↔screen projection.
|
||||
* `tweenChain.ts` — safe tween-chain registration.
|
||||
* `flashOverlayMaterial.ts` — additive hit-flash overlay.
|
||||
* `pickupFlight.ts` — shared drop → scatter → bounce → fly-to-target animation,
|
||||
driven by both `ResourceC` (wood) and `ZombiePartC` (zombie-part chunks).
|
||||
Never write a second one.
|
||||
|
||||
Контролери не імпортують одне одного напряму, коли це створить цикл — інжектиться
|
||||
геттер/колбек, а зв'язує `TestSceneC`:
|
||||
`ResourceC.setFlyTarget()`, `HudC.setBalanceGetter()`,
|
||||
`CameraFollowC.setFacingTargetGetter()`, `BreakablePropC.onHit/onDestroyed`.
|
||||
`HealthBarC` is shared by crates, zombies, and player. Do not create another
|
||||
health-bar implementation.
|
||||
|
||||
## Конфіг платформи ↔ код
|
||||
Two weapon progressions (bat level from wood, pistol level from zombie-part
|
||||
chunks), each with its own fixed Pay Zone (`PayZoneC`, instance-based — no
|
||||
relocation), and crate respawn: see `docs/progression.md`.
|
||||
|
||||
Платформа 24ADS рендерить ліву панель із категорій. Частина — наші
|
||||
(`src/configUIParams/`), частина — стандартні від SDK.
|
||||
`HealthBarOptions.variant` controls colors via `health-bar.css`.
|
||||
`follow` re-samples the anchor every frame for moving owners.
|
||||
Health bars appear on first hit and hide after `HIDE_DELAY_S`; full-HP entities
|
||||
do not show a bar.
|
||||
|
||||
| Вкладка на платформі | Джерело |
|
||||
|---|---|
|
||||
| Analytics params | `analytics.ts` |
|
||||
| Install Banner Settings | `installBanner.ts` |
|
||||
| **Scene settings** | `globalSettings.ts` (id категорії — `global`) |
|
||||
| Gameplay tuning | `gameplay.ts` |
|
||||
| Audio Settings | `sounds.ts` |
|
||||
| Screen Orientation | `orientation.ts` |
|
||||
| Filter scene, Redirect Settings, Loader Settings, Font Settings | SDK, не наш код |
|
||||
---
|
||||
|
||||
Читати значення в коді — `Template.getCategory("<id категорії>")["<id параметра>"]`
|
||||
(приклад — `DebugConfigC.init()`).
|
||||
## Dependency injection
|
||||
|
||||
**Scene settings** (`globalSettings.ts`): `light_intensity`, `light_color`,
|
||||
`camera_fov_p/l`, `camera_position_p/l`, `camera_rotation_p/l`.
|
||||
Controllers must not directly import each other when that would create a cycle.
|
||||
Inject getters/callbacks instead and wire them from `TestSceneC`.
|
||||
|
||||
**Формат `configUIParams` ≠ формат рантайму.** У визначенні кожна вісь масивного
|
||||
параметра — це `[min, max, default]`; у грі `Template.getCategory()` віддає
|
||||
готовий `[x, y, z]`. Не переплутати: `[-20, 70, -6]` — це не координати.
|
||||
Existing DI examples:
|
||||
|
||||
**Камера: рівно один писар на `camera.rotation` — `CameraFollowC`.**
|
||||
`camera_position_*` — це офсет **відносно гравця** (не абсолютна позиція, камера
|
||||
слідкує), `camera_rotation_*` — фіксований кут у градусах, `camera_fov_*` — FOV.
|
||||
Розподіл власності: FOV застосовує `CameraC.setCamera()`, риг —
|
||||
`CameraFollowC.applyRigFromConfig()`. Не повертати position/rotation у
|
||||
`CameraC.setCamera()`: він перезапускається на кожну зміну будь-якого `camera_*`
|
||||
поля, і два писарі на кут ламали камеру назавжди до кінця сесії.
|
||||
`CAMERA_OFFSET` у `TestSceneC` лишився тільки як фолбек.
|
||||
* `ResourceC.setFlyTarget()`
|
||||
* `HudC.setBalanceGetter()`
|
||||
* `CameraFollowC.setFacingTargetGetter()`
|
||||
* `BreakablePropC.onHit / onDestroyed / onRespawned`
|
||||
* `ZombieC.init(bounds, getPlayerPosition, damagePlayer, isPlayerAlive)`
|
||||
* `ZombieInvasionHudC.setOnComplete()`
|
||||
* `ZombiePartC.setFlyTarget()` / `PistolHudC.setLevelGetter()`
|
||||
|
||||
**Gameplay tuning** (`gameplay.ts`) дублює те, що дев-панель `DebugConfigC`
|
||||
(клавіша `C`, тільки lil-gui, не потрапляє в білд) показує локально. Панель
|
||||
сідить свої дефолти з цієї категорії при старті — один раз, без live-sync.
|
||||
`ZombieInvasionHudC.setOnComplete()` must be called **before** `init()`;
|
||||
`init()` immediately starts its countdown.
|
||||
|
||||
## Живі граблі
|
||||
---
|
||||
|
||||
Кожна вже кусала. Довгі розбори — в `docs/worklog/`.
|
||||
## Platform config
|
||||
|
||||
**glTF-завантажувач стирає крапки з імен нодів.** У сирому GLB `UI_Wood.001`, у
|
||||
живій сцені — `UI_Wood001`. `getObjectByName()` тихо повертає `undefined`, гард
|
||||
`if (node)` завжди хибний, код «виглядає правильним». Перевіряти реальне ім'я
|
||||
через `scene.traverse`, не через GLB JSON.
|
||||
24ADS renders config categories from `src/configUIParams/` plus SDK categories.
|
||||
|
||||
**Матеріали шаряться між усіма нодами з GLB.** Хочеш змінити матеріал одного
|
||||
об'єкта — клонуй його один раз при білді, інакше зачепиш усі 18 крейтів.
|
||||
| Platform tab | Source |
|
||||
| --------------------------------------- | ------------------------------ |
|
||||
| Analytics params | `analytics.ts` |
|
||||
| Install Banner Settings | `installBanner.ts` |
|
||||
| Scene settings | `globalSettings.ts` (`global`) |
|
||||
| Gameplay tuning | `gameplay.ts` |
|
||||
| Audio Settings | `sounds.ts` |
|
||||
| Screen Orientation | `orientation.ts` |
|
||||
| Filter scene / Redirect / Loader / Font | SDK |
|
||||
|
||||
**`material.color` на `MeshBasicMaterial` не дає флешу.** Матеріал unlit, колір
|
||||
множиться на текстуру, дефолт уже білий — лерп «білий → білий» невидимий.
|
||||
Для hit-flash — окремий адитивний оверлей-меш (`flashOverlayMaterial.ts`).
|
||||
Read runtime values with:
|
||||
|
||||
**«Чи гравець у зоні X» — тільки через `Box3().setFromObject(node)`.** Радіус на
|
||||
око зі скріншота не збігається з реальним футпринтом (і центр може бути
|
||||
несиметричний відносно пивота).
|
||||
|
||||
**Перспектива сама змінює видимий розмір.** Об'єкт, що летить до камери,
|
||||
«росте» без жодного дотику до `.scale`. Треба явно компенсувати —
|
||||
`perspectiveScale.ts`.
|
||||
|
||||
**Ціль польоту пере-семплювати щокадру.** Камера рухається разом з гравцем;
|
||||
взята один раз позиція HUD-анкера застигає й політ промахується.
|
||||
|
||||
**`tween.onComplete` зрідка не спрацьовує** (причина всередині tween.js не
|
||||
знайдена) — для критичного клінапу дублювати `setTimeout` на тривалість + запас.
|
||||
`removeFromScene` двічі — нешкідливо.
|
||||
|
||||
**screen↔world рахувати від `renderer.domElement.getBoundingClientRect()`**, не
|
||||
від `window.innerWidth/Height` — SDK рендерить у letterboxed 9:16 бокс.
|
||||
|
||||
**nipplejs `force` не обмежений радіусом зони** — за краєм кільця росте далі,
|
||||
хоч точка вже запінена. Клампити один раз при записі, не в кожному `calc()`.
|
||||
|
||||
**`Math.atan2` і CSS `conic-gradient` мають різний нуль** (схід vs північ) —
|
||||
різниця рівно 90°.
|
||||
|
||||
**Дві CSS-анімації на одному елементі не живуть разом** — перемикання класу,
|
||||
що чіпає `animation-name`, перезапускає анімацію. Розділяти на вкладені div.
|
||||
|
||||
**Анімувати RGB, не альфу**, якщо треба «згасання до базового кольору» — інакше
|
||||
на нулі отримаєш прозору дірку замість кольору.
|
||||
|
||||
**У CSS — `--lvh`/`--lvw`, не сирі `vh`/`vw`/`vmin`** (ротація/адаптивність).
|
||||
|
||||
**Експорт — це gzip-блоб, а не текст. НЕ грепати `export/*.html` напряму.**
|
||||
`npm run export` пакує весь бандл у base64+gzip (`decompressString("H4sIA...")`,
|
||||
розпаковується `pako` в рантаймі). Будь-який пошук по цьому файлу дасть **0
|
||||
збігів** — для всього, включно з класами самого SDK. Прочитати цей нуль як
|
||||
«purgecss вирізав наш CSS» — легка й дуже дорога помилка (перевірено: після
|
||||
розпаковки всі кастомні класи й `@font-face` на місці, кількість збігається з
|
||||
`vite build`). Перевіряти тільки після gunzip.
|
||||
|
||||
**purgecss справді вирізає `.bannerButton`** — але саме його, точково (клас
|
||||
призначається всередині мінифікованого SDK-бандла). У розпакованому експорті:
|
||||
`.bannerButton{` — 0, `.bannerLogo{` — 1, `.bannerButtonText{` — 1. Не писати
|
||||
CSS-правила під `.bannerButton`, це мертвий код. На решту класів не поширюється.
|
||||
|
||||
**`Template.setUpFont()` працює тільки коли `!release`** — тобто шрифт через
|
||||
`customFont.ts`/`formFontFamily` реєструється для **прев'ю платформи**, а не для
|
||||
експорту. У реальному білді шрифт малює наш власний `@font-face` з `main.css`
|
||||
(інлайн base64). Це два незалежні механізми на дві різні поверхні — потрібні
|
||||
обидва, і поломка одного не видна з іншого.
|
||||
|
||||
**Інлайн ассетів:** картинки — `ConvertToBase64WhenRelease("./file.webp")`, і
|
||||
викликати обов'язково **з файлу в тій самій директорії**, що й ассет. Шрифти —
|
||||
нічого не треба, `assetsInlineLimit` у конфігу SDK інлайнить `url()` у CSS сам.
|
||||
|
||||
## Ігрові дані
|
||||
|
||||
`ZombiePunk_Map.glb`, група **`Lootable`** → ~18 × `Wooden_Box_XXX`:
|
||||
```ts
|
||||
Template.getCategory("<categoryId>")["<parameterId>"]
|
||||
```
|
||||
Wooden_Box_XXX
|
||||
BoxCollider.NNN obstacle-бокс
|
||||
Wooden_Box_States_XXX
|
||||
Wooden_Box_XXX_S1..S3 стейджі пошкодження, не в усіх є всі три
|
||||
```
|
||||
Крейти можуть стартувати вже пошкодженими (тільки S2/S3) — `BreakablePropC`
|
||||
рахує стартовий HP з реальних номерів `_SN`, не з припущення про 3 стейджі.
|
||||
|
||||
`UI_Wood` — реальна іконка ресурсу (текстура `Icon_Wood`), клонується на кожен
|
||||
спавн. `UI_Interactive_Zone_02` — Pay Zone. `UI_Tool_Zone`, `UI_Wood001`, група
|
||||
`UI` — приховані як зайва декорація.
|
||||
### Config format
|
||||
|
||||
## Поточний стан
|
||||
`configUIParams` array parameters use `[min, max, default]` per axis.
|
||||
Runtime `Template.getCategory()` returns the actual `[x, y, z]` value.
|
||||
|
||||
Never interpret `[min, max, default]` as runtime coordinates.
|
||||
|
||||
### Camera ownership
|
||||
|
||||
There must be exactly one writer of `camera.rotation`: `CameraFollowC`.
|
||||
|
||||
* `CameraC.setCamera()` owns FOV only.
|
||||
* `CameraFollowC.applyRigFromConfig()` owns camera position/rotation.
|
||||
* `camera_position_*` is a player-relative offset, not an absolute position.
|
||||
* `camera_rotation_*` is a fixed angle in degrees.
|
||||
* `camera_fov_*` is FOV.
|
||||
* Do not write camera position/rotation from `CameraC.setCamera()`.
|
||||
`setCamera()` can run again when any `camera_*` config changes.
|
||||
* `TestSceneC.CAMERA_OFFSET` is fallback only.
|
||||
|
||||
### Gameplay tuning
|
||||
|
||||
`gameplay.ts` is the source for platform tuning defaults.
|
||||
`DebugConfigC` mirrors these values locally in lil-gui (`C` key, dev-only).
|
||||
The debug panel copies defaults once at startup; it does not live-sync.
|
||||
|
||||
---
|
||||
|
||||
## Critical SDK / runtime pitfalls
|
||||
|
||||
* GLTF loader removes dots from node names:
|
||||
`UI_Wood.001` → `UI_Wood001`.
|
||||
Inspect runtime names with `scene.traverse()`, not GLB JSON.
|
||||
|
||||
* GLB materials are shared between nodes. Clone a material before modifying
|
||||
one instance.
|
||||
|
||||
* `MeshBasicMaterial.color` cannot produce a visible white→white hit flash.
|
||||
Use `src/utils/flashOverlayMaterial.ts`.
|
||||
|
||||
* Zone checks must use `new Box3().setFromObject(node)`, not visual/radius estimates.
|
||||
|
||||
* Perspective changes apparent object size. Use `perspectiveScale.ts` when compensating
|
||||
for flight toward/away from the camera.
|
||||
|
||||
* Flight targets must be re-sampled every frame because the camera follows the player.
|
||||
|
||||
* Do not rely solely on tween `onComplete` for critical cleanup.
|
||||
Use a timeout fallback with a small safety margin.
|
||||
`removeFromScene()` is safe to call twice.
|
||||
|
||||
* screen↔world projection must use
|
||||
`renderer.domElement.getBoundingClientRect()`, never `window.innerWidth/Height`.
|
||||
SDK uses a letterboxed 9:16 viewport.
|
||||
|
||||
* `nipplejs.force` can exceed the visual joystick radius.
|
||||
Clamp it when storing input, not inside every calculation.
|
||||
|
||||
* `Math.atan2` and CSS `conic-gradient` use different zero directions.
|
||||
Account for the 90° offset.
|
||||
|
||||
* Do not stack independent CSS animations by changing `animation-name` on the
|
||||
same element. Use nested elements.
|
||||
|
||||
* To fade to a base color, animate RGB rather than alpha.
|
||||
|
||||
* Use `--lvh` / `--lvw`; avoid raw `vh` / `vw` / `vmin`.
|
||||
|
||||
---
|
||||
|
||||
## Export / CSS / fonts
|
||||
|
||||
* Export HTML is gzip+base64, not searchable source.
|
||||
Never grep `export/*.html` to verify bundled content.
|
||||
Inspect the decompressed payload instead.
|
||||
|
||||
* `.bannerButton` is purged from the release export.
|
||||
Do not add CSS targeting it.
|
||||
`.bannerLogo` and `.bannerButtonText` remain.
|
||||
|
||||
* `Template.setUpFont()` works only when `!release`.
|
||||
It is for platform preview.
|
||||
Release uses our own `@font-face` from `main.css`.
|
||||
|
||||
* Release images must use:
|
||||
`ConvertToBase64WhenRelease("./file.webp")`
|
||||
from the same directory as the asset.
|
||||
|
||||
* CSS fonts are inlined automatically by the configured `assetsInlineLimit`.
|
||||
|
||||
---
|
||||
|
||||
## Asset rules
|
||||
|
||||
`ZombiePunk_Map.glb`:
|
||||
|
||||
* `Lootable` contains approximately 18 `Wooden_Box_XXX` crates.
|
||||
* Crates may start at S2/S3. `BreakablePropC` must derive initial HP from
|
||||
actual `_SN` nodes; never assume three stages.
|
||||
* `UI_Wood` is the resource icon and must be cloned per spawn.
|
||||
* `UI_Interactive_Zone_02` is the pistol's Pay Zone marker (halved in scale);
|
||||
the bat's own Pay Zone is a runtime clone of it, not a second authored node
|
||||
— see `TestSceneC.createPayZoneNodes`.
|
||||
* `UI_Tool_Zone`, `UI_Wood001`, and `UI` are hidden decorative nodes.
|
||||
|
||||
`ZombiePunk_Character.glb`:
|
||||
|
||||
* Existing clips include `Idle`, `SlowWalk`, `Loot`, `WeaponTakeOut`,
|
||||
`PistolShoot`, `WeaponPutDown`, `Death`,
|
||||
`PistolBack/Forward/Left/Right`.
|
||||
* Do not invent character states before checking existing clips.
|
||||
* `Character_Pistol` is a skinned mesh (`skin:0`), not a bone prop.
|
||||
Skeleton animation moves it with the hand.
|
||||
* Bat uses `Tool_1` / `Tool_2` visibility switching.
|
||||
* `Bullet` is a separate hidden rig.
|
||||
|
||||
---
|
||||
|
||||
## Change discipline
|
||||
|
||||
* Prefer minimal, focused changes.
|
||||
* Do not refactor unrelated code.
|
||||
* Reuse existing utilities and controllers before creating new ones.
|
||||
* Preserve the existing DI pattern.
|
||||
* Before adding a new SDK workaround, check `src/utils/` and this file.
|
||||
* When a new persistent rule is discovered, update this file only if it affects
|
||||
future decisions; otherwise document it in the appropriate source file or
|
||||
`docs/worklog/`.
|
||||
|
||||
- Working tree брудний, багато незакомічених змін.
|
||||
- `DebugConfigC.ts` і `configUIParams/gameplay.ts` — нові, ще не в git.
|
||||
- **Відомі борги** (відкладені свідомо, за рішенням користувача):
|
||||
- `ui.css` / `health-bar.css` — досі на `vh`, без landscape-медіазапиту;
|
||||
- VFX-емітер `StretchSmoke` у `VFX_Lootable_Hit` вилітає далеко від крейта
|
||||
(дизайнерський ассет тюнили під інший масштаб сцени) — рішення за
|
||||
користувачем: лишити / притлумити / прибрати емітер;
|
||||
- Destroy-VFX підключений, але візуально не підтверджений.
|
||||
|
||||
Reference in New Issue
Block a user