105 lines
9.1 KiB
Markdown
105 lines
9.1 KiB
Markdown
# HUD-лічильник + перенаправлення анімацій (2026-08-12)
|
||
|
||
Користувач попросив: (1) справжню 2D UI-іконку ресурсу з лічильником у
|
||
правому верхньому куті, "як на скріні" (темна округла табличка + іконка в
|
||
круглій рамці зліва + число справа), (2) ресурс замість миттєвого
|
||
зникнення має анімовано летіти ДО цього лічильника, (3) політ у Pay Zone
|
||
має бути ВІД лічильника до зони (не від гравця).
|
||
|
||
## Іконка
|
||
Витягнув сам PNG/WebP з тієї ж текстури `Icon_Wood`, яку вже юзає 3D-меш
|
||
`UI_Wood` (`src/resources/meshes/ZombiePunk_Map.glb`, `bufferView` з
|
||
`images[6]`) — маленьким одноразовим Node-скриптом (парсинг glb JSON-чанка,
|
||
пошук `images[].name === "Icon_Wood"`, зріз байтів з BIN-чанка за
|
||
`bufferViews[bufferView]`). Зберіг як `src/resources/images/icon_wood.webp`
|
||
(webp з альфа-каналом, 512×512). Новий `src/resources/images/images.ts`
|
||
експортує `iconWoodSrc = ConvertToBase64WhenRelease("./icon_wood.webp")` —
|
||
**той самий паттерн, що і `meshes.ts`** (той же helper з `@hitplay/ads_common`,
|
||
викликаний з файлу в ТІЙ САМІЙ директорії, що й ассет — важливо: цей
|
||
хелпер в dev/build режимі просто рядково замінює провідний `.` на
|
||
`resources` (`ConvertToBase64WhenRelease.js`), а окремий AST-плагін
|
||
(`convertToBase64InAST`), підключений через `defineConfigTemplate` в
|
||
`vite.config.js`, на build-time переписує сам виклик у справжній inline
|
||
`data:...;base64,...` — це працює для ВСІХ режимів (dev/build/export), не
|
||
тільки для "export").
|
||
|
||
## HudC.ts — новий контролер
|
||
DOM-паттерн підглянутий у `InstallBanner` з SDK (немає спільного
|
||
DOM-builder helper-а в пакетах, усе руками через `document.createElement`
|
||
+ власний `<style>`, що вставляється в `<head>`): статичний клас, монтує
|
||
DOM-елемент у `#ui` (SDK-контейнер, `position:absolute`, зафіксований
|
||
9:16 blocks — `calc(100vh*9/16)` × `100vh`, центрований — це і є "екран
|
||
гри", не весь браузер, тож `top/right` у % рахуються відносно НЬОГО).
|
||
`.resource-hud { pointer-events:none }` — щоб не перехоплював тач/клік від
|
||
джойстика під ним. Стиль — власний CSS, вигаданий (в проєкті НЕ було
|
||
жодного готового "плата/банер" стилю для запозичення — перевірено, в
|
||
`ui.css`/`main.css` нуль хітів на `border-radius`).
|
||
|
||
Другий обов'язок `HudC` — **світова точка** (`worldAnchor`, порожній
|
||
`Object3D`, дитина камери через `CameraC_internal.getCamera().add(...)`,
|
||
локальний офсет — спочатку `(1.0, 0.8, -2.2)`, потім підкручено до
|
||
`(1.0, 1.2, -2.0)` (див. нижче, чому)), яка приблизно проєктується туди,
|
||
де візуально сидить DOM-іконка. **Це не точний screen-to-world розрахунок**
|
||
(FOV/aspect не враховані математично) — просто підібраний на око офсет;
|
||
якщо HUD переїде/зміниться розмір екрану, можливо треба підкрутити
|
||
координати ще раз. `getWorldAnchorPosition()` — це і є точка, куди тепер
|
||
летять ресурси (`ResourceC.setFlyTarget`) і звідки стартує депозит-політ
|
||
(`PayZoneC.playDepositFlight`).
|
||
|
||
## Що змінилось у ResourceC/PayZoneC
|
||
- `ResourceC`: `scatter` -> settle -> (якщо `flyTarget` заданий, а тепер
|
||
він завжди заданий — `HudC.getWorldAnchorPosition`) новий стейт
|
||
`toTarget` (0.5с, smoothstep + згасаюча дуга вгору, прямо до лічильника)
|
||
-> `collect()`. Без `flyTarget` — стара миттєва поведінка (fallback).
|
||
**На відміну від попередньої версії — НЕ телепортується на позицію
|
||
гравця перед польотом** (той крок був заточений під "летить З гравця",
|
||
зараз відповідь користувача про сам HUD не згадувала цей крок, тож
|
||
прибрав його — політ іде прямо з місця, де ресурс осів, до лічильника).
|
||
- `PayZoneC.playDepositFlight`: `from` тепер `HudC.getWorldAnchorPosition()`
|
||
замість позиції гравця — "від лічильника до пей зони", як попросив
|
||
користувач. Тригер (proximity до зони) не змінився — усе ще потребує
|
||
стояння гравця в зоні, змінилась лише точка ВИЛЬОТУ візуалу.
|
||
|
||
Перевірено в браузері (Playwright, scratchpad): іконка рендериться коректно
|
||
(512×512 webp, валідний `data:` URI, `naturalWidth`/`naturalHeight` не 0),
|
||
лічильник в DOM оновлюється синхронно з `console.log`-ами збору (звірено
|
||
скріншотом — "8" на екрані == 8-й `[ResourceC] gathered wood` в консолі).
|
||
|
||
## Доопрацювання: лічильник має бути "живим балансом", не lifetime-total (2026-08-12)
|
||
Спочатку `HudC` показував `ResourceC.getCollectedCount()` напряму — тобто
|
||
lifetime-суму, яка ТІЛЬКИ росте (депозит в Pay Zone на неї не впливав).
|
||
Користувач попросив: число має рости при зборі І **зменшуватись при
|
||
депозиті** — тобто показувати поточний "баланс", а не історичний тотал.
|
||
|
||
Розв'язання без нової мутабельної змінної (і без циклічного імпорту —
|
||
`PayZoneC` вже імпортує `HudC` для `getWorldAnchorPosition()`, тож
|
||
`HudC` імпортувати `PayZoneC` напряму означало б цикл):
|
||
- `PayZoneC.getDepositedCount()` — новий публічний гетер, повертає
|
||
внутрішній `deposited` (лічильник, який і раніше рахував "скільки вже
|
||
влетіло в зону", просто не був назовні доступний).
|
||
- `HudC.setBalanceGetter(fn)` — інжектований гетер (той самий паттерн, що
|
||
й `ResourceC.setFlyTarget`/`onDestroyed` в `BreakablePropC`), замінив
|
||
пряму залежність `HudC -> ResourceC`. `HudC` більше НЕ імпортує
|
||
`ResourceC` взагалі.
|
||
- `TestSceneC` зв'язує: `HudC.setBalanceGetter(() =>
|
||
ResourceC.getCollectedCount() - PayZoneC.getDepositedCount())` — чиста
|
||
похідна величина, рахується на льоту щокадру в `HudC.update()`, без
|
||
жодного окремого "decrement"-виклику. Росте коли `collected` росте
|
||
(щось зібрали), падає коли `deposited` росте (щось долетіло в зону) —
|
||
саме по собі, без спеціальної синхронізації.
|
||
|
||
Перевірено: зібрав 7 ресурсів (консоль: `gathered wood (7 total)`), весь
|
||
цей час стояв в Pay Zone -> усі 7 злились в зону протягом ~2.1с
|
||
(`DEPOSIT_INTERVAL=0.3` × 7) -> лічильник на екрані повернувся до **0**,
|
||
хоча `collected` лишився 7 — підтверджує, що баланс дійсно "живий", а не
|
||
lifetime-сума.
|
||
|
||
## Доопрацювання: точка вильоту ресурсів була занизько (2026-08-12)
|
||
`WORLD_ANCHOR_LOCAL_OFFSET` підняли з `(1.0, 0.8, -2.2)` до
|
||
`(1.0, 1.2, -2.0)` — більший Y (вище в camera-space) і трохи менший |Z|
|
||
(ближче до камери, тому той самий Y дає більше вертикальне зміщення на
|
||
екрані через перспективу) — все ще підібрано на око, не розраховано
|
||
математично з FOV/aspect. **Це найбільш "на око" підібрана частина
|
||
роботи — якщо після цього фіксу політ все ще не влучає точно в іконку,
|
||
підкрутити ці три числа ще раз** (в `HudC.ts`).
|