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