6.2 KiB
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-підхід видалений
повністю.