Files
onboarding-project/docs/worklog/18-camera-config-made-live.md
2026-08-14 15:03:43 +03:00

94 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Камера з конфігу платформи стала реальною (2026-08-14)
Слайдери `camera_position_p/_l` і `camera_rotation_p/_l` у Scene settings були
мертві: `CameraFollowC.init()` рахував позицію з константи `CAMERA_OFFSET` і
виводив кут одноразовим `lookAt()`, а конфіг не читав узагалі. Продюсер міг
крутити шість повзунків без жодного ефекту.
Вимога: повзунки прибирати не можна, вони мають працювати — і в експорті, і в
прев'ю платформи.
## Що з'ясувалось під час розбору
**Формат конфігу читається не так, як виглядає.** У `globalSettings.ts` кожна
вісь масивного параметра описана як `[min, max, default]`, тому
`camera_position_p` виглядав як три трійки координат. Насправді дефолт — це
третій елемент кожної трійки, а в рантаймі `Template.getCategory("global")`
віддає плаский `[x, y, z]`. Перевірено прогоном справжнього
`formConfigForPlayable(formConfigUI(...))` над нашим же `globalSettings`.
**Старі дефолти були з іншої моделі камери.** `[-2, 5, 42]` — це абсолютна
позиція камери зі статичного SDK-шаблону. У грі з follow-камерою абсолютна
позиція безглузда (її перезаписує кожен кадр), осмислений лише офсет відносно
гравця. Оскільки ці значення ніколи ні на що не впливали, від них нічого не
залежало — семантику переінтерпретовано на офсет.
**`Object3D.lookAt` і `Camera.lookAt` — різні.** Перша спроба вирахувати
еквівалентний кут дала `x=53.13, y=0, z=0` і виглядала правдоподібно, але
контрольна перевірка (зібрати кватерніон назад із цих кутів і зміряти
`forward · напрямок_на_ціль`) дала **−1.0** — камера дивилась рівно в
протилежний бік. Причина в самому three.js:
```js
if (this.isCamera || this.isLight) _m1.lookAt(_position, _target, this.up);
else _m1.lookAt(_target, _position, this.up);
```
Звичайний `Object3D` наводить на ціль свою вісь **+Z**, камера — **−Z**. Розрахунок
на `Object3D` замість `PerspectiveCamera` дає дзеркальний результат.
**Урок**: будь-який офлайн-розрахунок орієнтації перевіряти зворотним ходом
(відновити з чисел і зміряти скалярний добуток), а не приймати правдоподібні
числа на віру.
Справжній кут для офсету `(0, 8, -6)` — `x=-126.87, y=0, z=-180`. Еквівалентна,
але читабельніша форма `x=53.13, y=180, z=0` дає той самий кватерніон і той
самий up-вектор — саме її записано в дефолти. `y=180` тут не довільне: риг стоїть
позаду гравця на −Z і дивиться назад у +Z, а камера three.js за замовчуванням
дивиться в −Z.
Тип параметра — `int`, тож `53.13` округлено до `53`. Відхилення від старого
кадру — **0.13°**, візуально непомітно.
## Що змінено
- `globalSettings.ts` — тільки дефолти (id, назви, типи й діапазони не чіпані;
діапазони вже покривали нові значення). Портрет і ландшафт отримали однаковий
риг, бо гра й досі використовує один `CAMERA_OFFSET` для обох — тепер їх можна
розвести з платформи.
- `CameraFollowC` — новий `applyRigFromConfig()`: читає офсет і кут за поточною
орієнтацією, застосовує кут, лишається єдиним писарем `camera.rotation`.
`readVec3()` відсіює биті значення, і тоді кут виводиться старим способом
(`frameTargetAndStoreRotation()`) — зламаний конфіг деградує до «кадрує
гравця», а не до порожнього неба.
- `beforeResourcesLoadedCb` — `updateVariableConfig` тепер маршрутизує
`camera_position_*`/`camera_rotation_*` у `CameraFollowC`, FOV лишається за
`CameraC`. Роздільна власність — це і є захист від повторення бага з двома
писарями.
- `resizeCb` — на зміні орієнтації перечитує і FOV, і риг.
- `DebugConfigC` — повзунки кута/дистанції сідають із того самого конфігу
(`readCameraRigFromConfig()`), дубльована константа `DEFAULT_CAMERA_OFFSET`
прибрана. `setOffset()` і далі авто-кадрує й синхронізує `rotationDeg`.
- `TestSceneC.CAMERA_OFFSET` — лишився як фолбек, коментар оновлено.
## Перевірено
- `npx vite build` — 886 модулів, чисто.
- Прогін реального SDK-пайплайна: гра читає `camera_position_p = [0, 8, -6]`,
`camera_rotation_p = [53, 180, 0]`; відновлена з цих чисел камера дивиться на
гравця з похибкою 0.13°.
- `npx tsc --noEmit` у проєкті **не запускається** — `tsconfig.json` містить
`moduleResolution: node10`, яку TypeScript 7 видалив (TS5108). З обходом цієї
опції лишаються дві доіснуючі помилки, обидві не в зміненому коді:
deep-import `three/src/math/Color` у `ThreeC.ts` і застаріла декларація SDK для
`JoystickC.init({ color })`.
## Не перевірено
Візуально в браузері нічого не запускалось. Потрібен ручний прогін: старт у
портреті, поворот у ландшафт, правка повзунків на платформі наживо.
## Відкрите
Значення, збережені для вже наявних креативів на платформі, лишились у старій
(абсолютній) семантиці — їх треба одноразово перевиставити на офсет-значення.