11 KiB
Камера: прибрано 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 після ходьби/бою/розвороту персонажа — камера просто зсувається лінійно, кут не змінюється.