111 lines
11 KiB
Markdown
111 lines
11 KiB
Markdown
# PayZoneC: прогрес-заповнення зони + два реальні знайдені баги (2026-08-12)
|
||
|
||
Користувач попросив візуальний індикатор заповненості Pay Zone (скільки з
|
||
`RESOURCES_TO_CLOSE_ZONE` вже занесено). Три ітерації дизайну (кожна —
|
||
реальна правка коду, не просто обговорення):
|
||
1. Горизонтальний квад на землі, що росте вздовж world X — користувач:
|
||
"заповнюється... з права на ліво" (не те, що хотів).
|
||
2. Box, що росте вгору по Y (буквально "як вода в басейні") — виявився
|
||
концептуально хибним: верх/низ box-а завжди займають весь footprint
|
||
незалежно від висоти, тож з ізометричної камери зона виглядала "одразу
|
||
заповненою" на будь-якій висоті > 0.
|
||
3. **Фінал**: плаский квад на землі знову, але росте вздовж world Z (не X)
|
||
— з цієї камери (`CAMERA_OFFSET=(0,9,-9)`, дивиться в +Z) рух вздовж +Z
|
||
проєктується на екран як "знизу вгору", що і є тим "від краю до краю",
|
||
про яке просив користувач. Пінінг біля min-Z (найближчий до камери
|
||
край), колір `0x5ce65c` (зеленуватий).
|
||
|
||
## ⚠️ Знайдений і виправлений баг: депозит-політ інколи застигає в повітрі навіки
|
||
Користувач показав скріншот: статична текстура ресурсу, що просто висить
|
||
у повітрі. Трасував через тимчасове логування (spawn/complete ID на кожен
|
||
`playDepositFlight` — окремий `Tween`, не chain, коректно `TweenC.add()`-
|
||
нутий ПЕРЕД `.start()`) — підтвердив: рідко (не щоразу) `onComplete` просто
|
||
не спрацьовує, і клон застигає точно на прямій між HUD-точкою і зоною, на
|
||
довільному t. Причину всередині `tween.js`/`TweenC` не знайшов (це не той
|
||
самий баг з `.chain()` з Day 6 — тут узагалі нема chain). **Замість
|
||
подальшого копання — надійний дублюючий `setTimeout`**, що прибирає mesh
|
||
через `DEPOSIT_FLIGHT_DURATION_MS + 300мс` незалежно від того, чи
|
||
спрацював `onComplete`. Викликати `removeFromScene` двічі — нешкідливо
|
||
(другий викоик просто не знаходить батька для detach).
|
||
|
||
## ⚠️ Знайдений і виправлений баг: `"UI_Wood.001"` в коді не збігається з реальною назвою ноду
|
||
Користувач наполягав, що "текстура дерева" в центрі Pay Zone нікуди не
|
||
зникає, хоча по черзі виключив усі мої гіпотези (крейти всередині зони,
|
||
застиглий ресурс). Трасував живу сцену (`ThreeC.scene.traverse`, шукаючи
|
||
все з "UI_Wood" в імені, друкуючи ланцюжок `visible` від ноду до кореня) —
|
||
і ось воно: нод з raw glb JSON названий `"UI_Wood.001"` (з крапкою,
|
||
підтверджено прямим парсингом бінарника), але в ЖИВІЙ Three.js сцені після
|
||
завантаження він `"UI_Wood001"` (**без крапки** — щось у пайплайні
|
||
завантаження її стирає). `TestSceneC.createMap()` шукав
|
||
`map.getObjectByName("UI_Wood.001")` — рядок ніколи не збігався,
|
||
`getObjectByName` тихо повертав `undefined`, `if (woodIconAlt)` була
|
||
`false`, і `.visible=false` НІКОЛИ фактично не виконувався — попри те, що
|
||
код виглядав абсолютно правильним і "мав" би працювати. Виправлення:
|
||
рядок-літерал змінено на `"UI_Wood001"` (без крапки), підтверджено
|
||
трасуванням живої сцени, що тепер `visible=false` реально застосовується.
|
||
**Урок**: коли `if (identifiedNode)`-гард ЗАВЖДИ хибний (вузол ніби існує
|
||
в GLB, але код його "не бачить") — перевіряти РЕАЛЬНЕ ім'я в ЖИВІЙ сцені
|
||
(`scene.traverse` + лог імені), не тільки в сирому GLB JSON — завантажувач
|
||
може мовчки переписувати рядки (крапки, ймовірно, конфліктують з якоюсь
|
||
внутрішньою угодою іменування).
|
||
|
||
Побічно: під час полювання на цей баг зробив (і одразу відкотив на
|
||
прохання користувача) дві помилкові гіпотези-фікси — приховання
|
||
`UI_Interactive_Zone_02` (сам маркер зони) і видалення `Wooden_Box_016`/
|
||
`Wooden_Box_017` (2 крейти, що геометрично сидять майже точно в центрі
|
||
зони, ~0.18 і ~0.99 одиниць від центру — підтверджено обчисленням world
|
||
position прямо з GLB node transforms). Жодна з цих гіпотез не була
|
||
причиною — обидві повернуто як були. Крейти в зоні — це може бути окрема,
|
||
самостійна проблема левел-дизайну (вони справді там стоять), але це
|
||
свідоме рішення НЕ трогати без окремого прямого запиту користувача.
|
||
|
||
- Working tree на момент старту Day 5: `PlayerC.ts` мав незакомічені правки
|
||
(accel/decel рух, рефакторинг attack-facing на helper-и) — вони увійшли в
|
||
фінальну версію файлу (переписаний повністю, логіка руху збережена).
|
||
- Свідоме рішення: не рефакторити робочий AABB рух гравця під фізику; після
|
||
видалення тригерів у гравця взагалі немає cannon `Body` — тільки AABB.
|
||
- `ResourceC`/`BreakablePropC` — нові файли. `PlayerC.ts`/`TestSceneC.ts`/
|
||
`PhysicsC.ts` — модифіковані. `CameraFollowC.ts`/`ThreeC.ts` — не торкались.
|
||
`TriggerC.ts` — був створений і видалений в межах цієї ж сесії (див. "Файли").
|
||
- Закомічено й запушено в `origin/master` 2026-08-12 (commit `4a22a9b`,
|
||
"Add physics/combat foundation, resource pickup flow, VFX polish, and
|
||
HUD") — усе, що описано в цьому файлі до Day 8 включно, вже в репо.
|
||
`stats.html` (rollup-visualizer, артефакт білда) свідомо НЕ закомічено —
|
||
не вихідний код, генерується заново при білді.
|
||
- Якщо наступного разу знову треба буде правити combat: зона ураження —
|
||
та сама `INTERACTION_REACH`-аура, що й раніше використовувалась для
|
||
single-target пошуку (`findNearbyObstacle`, лишився для косметичного
|
||
idle-facing), просто тепер є окремий `findBreakableTargetsInZone` без
|
||
обмеження на кількість цілей.
|
||
- Day 6 (Tween): `TweenC.init()` викликається один раз в
|
||
`beforeResourcesLoadedCb.ts` — якщо колись переносити фізику/tween-бутстрап
|
||
в інше місце, не забути перенести й це, інакше всі `TweenC.add()` тихо
|
||
ні на що не впливають (сама `Group` існує, просто ніхто її не `.update()`-ить).
|
||
- `PayZoneC.init(payZoneNode, target)` МАЄ викликатись після `PlayerC.init()`
|
||
в `TestSceneC` (потребує реальний `PlayerC.object`, не просто позицію) —
|
||
уже виправлено (виклик з `TestSceneC.init()`, не з `createMap()`), просто
|
||
важливо не переносити назад.
|
||
- **Будь-який новий `.chain()` в цьому проєкті — додавай ОБИДВІ (усі) ланки
|
||
в `TweenC` окремо** (`TweenC.add(a); TweenC.add(b); a.start();`), інакше
|
||
повториться баг вище: друга ланка "грає" за прапорцем, але update() на
|
||
неї ніхто не кличе, і вона застигає навічно.
|
||
- **Важливий урок з цієї сесії**: "збір ресурсу" (`ResourceC`) і "передача
|
||
в Pay Zone" (`PayZoneC`) — два НЕЗАЛЕЖНІ механізми, і мають лишатись
|
||
такими. Не гейтувати `ResourceC.collect()` жодною умовою про гравця/зону
|
||
— це те, що вже одного разу зламало базовий збір (ресурси лежали на
|
||
землі й не зникали). Будь-яку майбутню умову про "стояння в зоні" чіпляти
|
||
тільки на стороні `PayZoneC` (він і так окремо стежить за
|
||
`getCollectedCount()` — `deposited`, тобто "хвостом" з незданого).
|
||
- **Ще один урок**: не вгадувати розмір/радіус тригер-зони на око зі
|
||
скріншота — рахувати з реальної геометрії (`Box3().setFromObject(node)`).
|
||
Саме це зламало `PayZoneC.isPlayerInside` (радіус=2 замалий за фактичний
|
||
прямокутник ~6×4.6, ще й не центрований на origin).
|
||
- Ручна перевірка (флеш/нахил крейтів, повне зникнення після 2 хітів на
|
||
стейдж, базовий збір ресурсів без прив'язки до зони, злив ресурсів у Pay
|
||
Zone тільки при стоянні там — тепер по реальному Box3, не вгаданому
|
||
радіусу, зникнення зони після `RESOURCES_TO_CLOSE_ZONE` доставлених
|
||
(100 на момент цього запису, зараз 20 — див. "прогрес-заповнення зони"),
|
||
відсутність зайвих текстур навколо Pay Zone) — стан на кінець Day 5/6;
|
||
усе, що сталось після (HUD, камера, VFX, прогрес-бар зони), має власні
|
||
"Перевірено" нотатки нижче за текстом.
|