70 lines
5.9 KiB
Markdown
70 lines
5.9 KiB
Markdown
# 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 чекліста, відкладений на потім.
|