70 lines
6.2 KiB
Markdown
70 lines
6.2 KiB
Markdown
# Day 8: UI — резорс-каунтер (2026-08-12)
|
||
|
||
Референс — скріншот іншої гри ("Zombie Invasion") з двома стековани
|
||
лічильниками (дерево + метал); у нас лише один тип ресурсу, тож зроблено
|
||
тільки один лічильник, без плейсхолдера під другий (за проханням
|
||
користувача, поки не підтверджено, що другий ресурс дійсно буде).
|
||
|
||
## Реальний дизайнерський ассет замість CSS-пілла
|
||
Користувач: "Tool_15 знайди цей файл в temp. Це юай для каунтера
|
||
ресурсу." `temp/Tool_15.webp` (184×71, темна округла плата з діагональною
|
||
деревʼяною планкою, вже вбудованою в саму картинку) — скопійований у
|
||
`src/resources/images/resource_counter_bg.webp`, підключений через
|
||
`images.ts` (`resourceCounterBgSrc`, той самий `ConvertToBase64WhenRelease`-
|
||
паттерн). Стара кругла іконка-рамка + окремий `<img>` (з `icon_wood.webp`)
|
||
прибрані повністю — планка вже частина фонового зображення.
|
||
`icon_wood.webp`/`iconWoodSrc` після цього стали осиротілими і видалені.
|
||
|
||
## Стилі винесені в `ui.css`, живлені реальним аспектом і CSS-змінною
|
||
Користувач: "Всі стилі для юай мають бути в css файлі, у нас є відповідний
|
||
файл вже" — весь inline `document.createElement("style")` з `HudC.ts`
|
||
прибраний, стилі перенесені в `src/css/ui.css` (вже підключений в
|
||
`index.html`). `HudC.ts` лишає в JS тільки `holder.style.backgroundImage`
|
||
(шлях до ассету відомий лише в runtime через build-пайплайн, статичний
|
||
CSS-файл цього не може). Розмір плейта: спочатку `width`+`height` окремо
|
||
в `vh` (руками підібрана пропорція 184:71) — користувач: "не подобається,
|
||
що задана і висота і ширина, давай контролювати через аспект рейтіо" ->
|
||
`aspect-ratio: 184/71` замість другого числа. Потім: CSS custom property
|
||
`--hud-width` на `.resource-hud`, і `font-size: calc(var(--hud-width) *
|
||
0.175)` замість фіксованого `vh` — користувач змінював `width`, шрифт не
|
||
слідував; тепер один параметр (`--hud-width`) керує і розміром плати, і
|
||
шрифтом (успадковується вниз по DOM).
|
||
|
||
## Дві CSS-анімації замість JS-коду (Day 8 технічна вимога)
|
||
"Use built-in animations/transitions instead of changing elements through
|
||
code" — (1) `.resource-hud` грає `resource-hud-enter` (fade+slide) один
|
||
раз при монтуванні, самостійно, без JS-таймингу; (2) `.resource-hud__plate`
|
||
(НЕ сам `.resource-hud` — окремо, дивись нижче чому) грає
|
||
`resource-hud-bump` при кожній зміні числа — `HudC.update()` лише
|
||
toggle-ить клас `is-bumping` (remove -> force reflow через `offsetWidth`
|
||
-> add), сам keyframe рахує браузер.
|
||
|
||
**⚠️ Знахідка: анімацію не можна вішати на ТОЙ САМИЙ елемент, що вже має іншу**
|
||
Спочатку bump-анімація була на `.resource-hud__count` (сам текст) —
|
||
користувач: "Анімація має бути на всю юайку не тільки на каунтер". Але
|
||
просто перенести на `.resource-hud` (той самий елемент, що вже грає
|
||
`resource-hud-enter`) НЕ можна: перемикання класу, що змінює
|
||
`animation-name` (навіть тимчасово, назад до того самого значення),
|
||
ЗАВЖДИ перезапускає анімацію — тобто кожен збір ресурсу знову
|
||
проганяв би fade+slide-in ("з'явлення") поверх бампу. **Фікс**: два
|
||
вкладені div — зовнішній `.resource-hud` (позиція/розмір + enter-анімація,
|
||
статична), внутрішній `.resource-hud__plate` (фон+число разом,
|
||
bump-анімація) — дві незалежні `animation`-лінії на двох елементах.
|
||
|
||
## `getWorldAnchorPosition()` — справжня screen-to-world проєкція
|
||
Раніше (Day "HUD-лічильник"): фіктивний `Object3D`, дитина камери, з
|
||
вгаданим локальним офсетом (`WORLD_ANCHOR_LOCAL_OFFSET`, підбирався на
|
||
око, документований як "не точний screen-to-world розрахунок").
|
||
Користувач: "Треба дістати позицію правого края юайки і направляти
|
||
ресурси саме туди." **Фінал**: `getBoundingClientRect()` правого краю
|
||
`.resource-hud__plate` (реальний DOM), конвертація в NDC відносно
|
||
`ThreeC.renderer.domElement.getBoundingClientRect()` (canvas, НЕ
|
||
`window.innerWidth/Height` — SDK рендерить у letterboxed 9:16 бокс,
|
||
canvas-rect гарантовано збігається з тим, що бачить камера),
|
||
`new Vector3(ndcX,ndcY,0.5).unproject(camera)` -> напрямок від камери ->
|
||
точка на фіксованій `ANCHOR_DISTANCE=2.5` вздовж цього променя.
|
||
Рахується ЖИВО при кожному викликі (не раз при init) — автоматично
|
||
лишається коректним при resize/зміні орієнтації, без окремого resize-
|
||
слухача. `Object3D`/`WORLD_ANCHOR_LOCAL_OFFSET`-підхід видалений
|
||
повністю.
|