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

6.2 KiB
Raw Blame History

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