Files
onboarding-project/docs/worklog/13-health-bars.md
T
2026-08-14 15:03:43 +03:00

70 lines
5.9 KiB
Markdown
Raw 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.
# Health bar для дестройбл-елементів (2026-08-13)
Референс-скріншот: прямокутний хелз бар з бордером, синя заливка HP,
чорним — вже відняте попередніми ударами, білим — відняте саме поточним
ударом (з коротким затуханням у чорне), число нанесеного урону над баром.
Нехай 10 урону за удар, 100хп у цілого ящика.
## `BreakablePropC` — перехід з дискретного лічильника хітів на HP
`HITS_PER_STAGE`/`hitsOnStage` повністю видалені. `BreakableProp` тепер
несе `currentHp`/`maxHp` (`MAX_HP = 100`, `export`-нутий, щоб `HealthBarC`
рахував той самий `0..1` дріб без дублювання числа) + `startStageNumber`/
`totalPossibleStages` — реальні номери з `_SN` у назвах стейдж-нод (не
вигадані), з яких рахується, скільки HP пошкоджений з коробки крейт вже
має "по дефолту" (пошкоджені S2/S3-старт крейти стартують не з 100).
`hit()` тепер: відняти `HIT_DAMAGE`, повідомити `HealthBarC.onHit(...)`,
знищити на HP<=0 (раніше — "просунувся за останній стейдж"), інакше —
перевести `stageIndex` на `Math.floor((1 - currentHp/maxHp) *
totalPossibleStages)` (з відступом на `startStageNumber`), змаплений у
межі власного (можливо короткого) масиву стейджів.
## `HealthBarC.ts` — новий контролер, world-tracking DOM-бари
Одна мапа `BreakableProp -> BarInstance`, живиться з `BreakablePropC.hit()`
через `onHit`/`onDestroyed` (аналогічний DI-паттерн до `ResourceC.setFlyTarget`).
**Позиціювання**: `position:fixed` + сирі px з `canvasRect` (та ж техніка,
що й `HudC.getWorldAnchorPosition`, лише в протилежний бік — тепер
винесена в `src/utils/screenWorldProjection.ts`, обидва напрямки). Точка
прив'язки — реальний `Box3().setFromObject(prop.root)` (верх+центр X/Z),
не вгаданий офсет.
**Три кольори — чистий CSS, без пофреймової JS-анімації**: `.health-bar__white`
й `.health-bar__blue` — два накладені div. На кожен хіт: білий миттєво
(`transition-duration: 0s`) стрибає на ПОПЕРЕДНІЙ `%`, форс-reflow через
`offsetWidth`, потім і білий (з `transition-delay`), і синій одразу
виставляються на ПОТОЧНИЙ `%` — синій доїжджає без затримки, білий
"зависає" на старому значенні й наздоганяє з лагом, показуючи чорний трек
під собою в проміжку. Ніякого `requestAnimationFrame`/tween — сама
CSS-transition timing і є ефектом.
**Авто-приховування**: `idleTime` рахується щокадру, `>= HIDE_DELAY_S` (2с)
без нового хіта — `is-hiding` клас + `FADE_OUT_MS` (250мс) opacity-transition,
потім видалення з DOM. Скидається на 0 при кожному `onHit`. `onDestroyed`
видаляє бар негайно (порожній бар не має лишатись висіти).
**Числа урону — кілька раундів фідбеку, фінал**:
- Спочатку плавали вгору з затуханням — користувач попросив натомість
різку появу непрозорого числа (`-webkit-text-stroke`, не text-shadow-глов)
з різким скейл-панчем до 1.5x + ротейтом і поверненням, і тільки ПОТІМ
затуханням (не рухом).
- Ротейт — послідовно звужувався: спершу довільний напрямок/кут, потім
користувач уточнив "тільки по годинниковій стрілці, до 2 години дня"
(тобто 0..60°, `--damage-rotate`, рандомізується в JS на кожен спавн,
читається в keyframe через `var()`).
- **Баг знайдений і виправлений**: затухання на спокої (force=0, у
контексті джойстика — не плутати з цим, тут йдеться про сам панч)
показувало прозору прогалину замість базового кольору, бо альфа
інтерполювалась до 0 — фікс: альфа фіксована, змінюється лише
RGB-канал (той самий принцип пізніше повторився і в джойстику, див.
нижче).
- Тривалість/розмір/швидкість анімації (920мс, +20% розмір шрифту,
expo-out крива "швидкий старт — повільний кінець") — прямі числові
правки за проханням користувача.
## `src/css/health-bar.css` — новий файл
Слідує тому ж house convention, що й `ui.css` (одна `--health-bar-width`
змінна, `--lvh` не сирий `vh`) — **окремо зазначено користувачем не
чіпати**: файл (як і `ui.css`) свідомо не отримав landscape
`@media`-блоку і досі на `vh`, не `rem`/`%` — відомий, прийнятий
відступ від Day 8 чекліста, відкладений на потім.