Files
onboarding-project/docs/worklog/08-payzone-fill-progress.md
2026-08-14 15:03:43 +03:00

11 KiB
Raw Permalink Blame History

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, прогрес-бар зони), має власні "Перевірено" нотатки нижче за текстом.