Files
onboarding-project/docs/worklog/10-day8-resource-counter.md
2026-08-14 15:03:43 +03:00

70 lines
6.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`-підхід видалений
повністю.