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

5.9 KiB
Raw Blame History

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