Utility and polishing + bag fixes

This commit is contained in:
Oleksandr Vlasiuk
2026-08-14 15:03:43 +03:00
parent 250f4a0d2c
commit 1f72420da0
45 changed files with 2542 additions and 1618 deletions
+120
View File
@@ -0,0 +1,120 @@
# Камера: прибрано `lookAt`, лінійне зміщення замість орбіти (2026-08-12)
Фідбек від ментора користувача: замість `lookAt`-based слідкування камери
за персонажем — краще визначити точку біля персонажа і зміщувати камеру
відносно неї, залежно від напрямку погляду персонажа. Уточнення від
користувача: **без orbit-behind** — це має бути суто лінійне зміщення
позиції, кут камери взагалі не повинен обертатись.
## Що було
`CameraFollowC.update()` щокадру: (1) демпінгував позицію камери до
`targetPosition + offset + lookAhead` (це вже було чисте лінійне
зміщення — `lookAhead` — той самий "зсув в напрямку погляду персонажа",
про який казав ментор, він вже існував), (2) **окремо** демпінгував
`smoothedLookAt`-точку і кожен кадр робив `camera.lookAt(smoothedLookAt)`
— тобто поворот перераховувався з нуля щокадру з двох незалежно
згладжених точок, а не одного узгодженого стану.
## Що зроблено (перша ітерація)
- Прибрано `smoothedLookAt`, `LOOK_DAMPING`, і сам виклик `camera.lookAt()`
з `update()` повністю. Позиційна частина (`offset` + `lookAhead` +
obstacle-avoidance raycast) лишилась незмінною — вона й раніше була
чистим translation, без жодного обертання.
- Поворот камери відтепер **виставляється один раз** в `init()`:
`camera.position.copy(target.position).add(offset); camera.lookAt(target.position);`
— і після цього `update()` більше НІКОЛИ не торкається `camera.rotation`/
`camera.quaternion`. Кут "заморожений" геометрично на старті й лишається
таким назавжди, скільки б персонаж не розвертався.
## Доопрацювання: навіть без обертання камери відчувався рух "по колу" (2026-08-12)
Користувач: коли персонаж РОЗВЕРТАЄТЬСЯ (не рухаючись при цьому), камера
все ще відчутно "йде по колу". Причина — той самий `lookAhead`-вектор, що
лишили в першій ітерації: він рахувався з напрямку, куди персонаж
**дивиться** (`facing`, з `target.quaternion`), а не куди рухається.
Навіть згладжений (`LOOK_AHEAD_DAMPING`), він все одно ОБЕРТАЄТЬСЯ разом
з поворотом персонажа (наприклад, коли той розвертається на місці, щоб
глянути на ящик) — а що обертається, те й тягне позицію камери по дузі
навколо персонажа, хай і невеликій. Користувач хотів буквально "2 точки і
пряма між ними" — жодної залежності від напрямку погляду.
**Перша спроба**: прибрав `lookAhead` повністю (`desiredPosition =
targetPosition + offset`, без жодного додаткового вектора). Круговий рух
дійсно зник, але користувач одразу зауважив: зникло й саме зміщення —
"раніше зміщення було підходяще, єдина проблема була в тому, що воно йшло
по колу". Тобто магнітуда/факт панорамування був потрібен, просто НЕ
прив'язаний до повороту.
**Фінальне виправлення**: `lookAhead` вернув, але тепер рахується з
**реального зміщення позиції персонажа між кадрами**
(`targetPosition - previousTargetPosition`), а не з `target.quaternion`.
`previousTargetPosition` — нове поле, оновлюється щокадру. Якщо кадровий
рух менший за `MOVEMENT_EPSILON_SQ` (стоїть на місці — байдуже, як
розвернутий) — цільовий look-ahead = нульовий вектор; якщо рухається —
нормалізований напрямок руху × `LOOK_AHEAD_DISTANCE` (та сама магнітуда,
що й була). Обидва варіанти йдуть через той самий `smoothedLookAhead.lerp`
з `LOOK_AHEAD_DAMPING`, як і раніше — тільки джерело напрямку інше.
Результат: розворот на місці = нуль руху = камера взагалі не рухається;
ходьба = той самий пан вперед, що й був до всіх цих правок.
**Урок**: "згладжений вектор, що обертається з об'єктом" все одно
читається як орбітальний рух — згладжування прибирає різкість, а не
кривизну; правильний фікс — прив'язати джерело напрямку до РУХУ
(position delta), а не до facing/quaternion, а не просто видалити ефект.
Перевірено в браузері (Playwright) — рендер коректний з першого кадру і
після ходьби/бою, без console-помилок.
## Доопрацювання ×3: зовсім не відчувалось, тримати зсув, миттєвий розворот на 180° (2026-08-12)
Після руху-based lookahead користувач: "Немає зміщення взагалі". Причина —
не баг у логіці (перевірив логуванням, числа рахувались вірно), а те, що
**стара `lookAt`-версія давала подвійний ефект** (і зсув позиції, і
доворот камери в той же бік щокадру), а зараз лишився тільки зсув позиції
— і сама позиційна складова (`LOOK_AHEAD_DISTANCE=1.2`) була занадто
малою, щоб щось означати з відстані ~12.7 од. (`(0,9,-9)` риг). Підняв до
`4`, користувач сам потюнив назад до **`2`** (лишив цю зміну, не
відкатувати).
Ще дві правки в тому ж повідомленні:
- **"Камера після зсуву не має повертатись назад"** — `desiredLookAhead`
тепер **окреме поле**, не локальна константа: оновлюється (`.copy(movement)...`)
ТІЛЬКИ коли `movement.lengthSq() > MOVEMENT_EPSILON_SQ`, і просто НЕ
чіпається, коли персонаж стоїть — замість `: new Vector3()` (нуль) в
тернарному операторі, як було. Тобто пан тримається на місці, де
зупинився, а не з'їжджає назад до центру, поки не з'явиться новий, ІНШИЙ
напрямок руху.
- **"Зроби анімацію плавнішою"** — `LOOK_AHEAD_DAMPING` знижений з `2` до
`1.2` (повільніший ease).
- **"Персонаж має розвертатись моментально, якщо ми змінюємо напрям на
протилежний"** — це вже не про камеру, а про `PlayerC` (`updateMovement`):
новий `OPPOSITE_TURN_THRESHOLD = Math.PI * (150/180)` (~150°). Якщо кут
між поточним і цільовим facing (`quaternion.angleTo(...)`) перевищує цей
порог — `quaternion.copy(facingRotation)` миттєво, інакше — старий
капований `rotateTowards(facingRotation, MAX_TURN_SPEED * delta)`. Тобто
тільки різкі розвороти "в протилежну сторону" миттєві, дрібні корекції
курсу лишаються плавними, як і були.
## ⚠️ Знахідка: `camera_rotation_p`/`camera_rotation_l` (дизайнерський конфіг) виявився нежиттєздатним самостійно
Спочатку спробував залишити кут камери таким, яким його виставляє
`CameraC.setCamera()` з конфіг-параметрів `camera_rotation_p`/`_l`
(дизайнер може їх тюнити через UI) — просто прибравши `lookAt` і НІЧОГО
більше не додаючи. Результат: **порожнє темно-синє небо**, ні мапи, ні
персонажа в кадрі. Причина: цей конфіг-кут ніколи насправді не був
призначений працювати самостійно — `CameraFollowC` з першого ж комміту,
що його додав, миттєво перезаписував поворот через `lookAt()` щокадру,
тож ніхто й не міг помітити (чи потребу) тюнити `camera_rotation_p` під
реальний рух — він був "мертвим" параметром, що впливав щонайбільше на
один кадр до першого `update()`.
**Виправлення**: не покладатись на цей конфіг взагалі для follow-камери —
рахувати фіксований кут геометрично з реального `offset` (`(0,9,-9)`) в
момент `CameraFollowC.init()` (описано вище). Це гарантовано framing
персонажа з самого старту, незалежно від того, що зараз стоїть в
`camera_rotation_p`. **Урок**: значення з `configUIParams`/`globalSettings`
не завжди відображають те, що реально відбувається в грі — перевіряти,
чи щось інше (як тут `CameraFollowC`) не перезаписує їх одразу після
встановлення, перш ніж покладатись на них як на джерело правди.
Перевірено в браузері (Playwright): після фіксу сцена рендериться коректно
з самого старту (мапа/персонаж/крейти на місці, кут ідентичний
попередньому вигляду), і лишається так само коректно framed після
ходьби/бою/розвороту персонажа — камера просто зсувається лінійно, кут
не змінюється.