94 lines
7.1 KiB
Markdown
94 lines
7.1 KiB
Markdown
# Камера з конфігу платформи стала реальною (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 })`.
|
||
|
||
## Не перевірено
|
||
|
||
Візуально в браузері нічого не запускалось. Потрібен ручний прогін: старт у
|
||
портреті, поворот у ландшафт, правка повзунків на платформі наживо.
|
||
|
||
## Відкрите
|
||
|
||
Значення, збережені для вже наявних креативів на платформі, лишились у старій
|
||
(абсолютній) семантиці — їх треба одноразово перевиставити на офсет-значення.
|