# Движок студии Рублокс Это сердце [studio.rublox.pro](https://studio.rublox.pro). Тут живёт всё: рендер Babylon-сцены, физика, скрипты пользователей, мультиплеер, ландшафт, ассет-менеджмент. Для контрибьютора, который впервые видит этот код — читай этот README сверху вниз, дальше доки по подпапкам. ## 1. Слои движка Снизу вверх: ``` ┌─────────────────────────────────────────────────────┐ │ React UI (panels, тулбары) │ ├─────────────────────────────────────────────────────┤ │ GameRuntime ← оркестратор «игрового │ │ PlayerController режима» (когда игрок │ │ ScriptSandbox(Worker) жмёт Play в редакторе) │ ├─────────────────────────────────────────────────────┤ │ Менеджеры объектов сцены │ │ BlockManager — блоки 1×1×1 │ │ PrimitiveManager — сферы/цилиндры/конусы │ │ ModelManager — GLB-модели из библиотеки │ │ UserModelManager — модели юзера (CAD-редактор) │ │ NpcManager — NPC и враги │ │ DecoManager — трава/цветы/деревья │ ├─────────────────────────────────────────────────────┤ │ Системы поверх сцены │ │ PhysicsWorld — AABB + интеграция Хейуна │ │ SelectionManager — клик → подсветка │ │ GizmoController — translate/rotate/scale gizmos │ │ HistoryManager — Ctrl+Z / Ctrl+Y │ │ MultiplayerSync — Colyseus state-sync │ ├─────────────────────────────────────────────────────┤ │ Низ: BabylonScene = Engine + Scene + Camera │ │ TerrainManager + voxel/ │ └─────────────────────────────────────────────────────┘ ``` `BabylonScene.js` — точка входа. Создаёт `Engine`, `Scene`, `UniversalCamera` с Roblox-style контролами (ПКМ + WASD-полёт), подключает всех менеджеров. ## 2. Как игрок ставит блок (полный путь) Это самый частый кейс. Понимаешь его → понимаешь как работают все остальные менеджеры. ``` 1. User жмёт ЛКМ в редакторе ↓ 2. BabylonScene.onCanvasPointerDown - делает ray из камеры через курсор - scene.pickWithRay() → выясняет, в какую грань какого меша попали ↓ 3. Если активен инструмент «Кисть блоков»: BlockManager.placeBlock(pos, blockType) - вычисляет grid-aligned позицию (округление к 1×1×1) - создаёт InstancedMesh от шаблонного меша блока (один draw call на тип) - регистрирует в внутренней карте: pos.x|y|z → mesh - HistoryManager.push({ action: 'place', pos, type }) ↓ 4. Если активен Multiplayer: MultiplayerSync.sendBlockPlaced({ pos, type }) - летит в Colyseus-комнату - сервер броадкастит остальным игрокам - у них BlockManager.placeBlock() вызывается снова, но без HistoryManager.push ``` ## 3. Менеджеры — зачем их столько Каждый менеджер владеет одной категорией объектов. Менеджеры **не знают друг о друге** — все общаются через `BabylonScene` (он же `scene`-объект, проброшен в каждый менеджер при init). | Менеджер | Что хранит | Ключевой метод | |---|---|---| | **BlockManager** | блоки grid-aligned | `placeBlock(pos, type)` / `removeBlock(pos)` | | **PrimitiveManager** | сферы/цилиндры/конусы любого размера | `addPrimitive({ type, pos, size, color })` | | **ModelManager** | GLB-модели (мечи, деревья, машины) | `addModel({ modelId, pos, rotation, scaling })` | | **UserModelManager** | модели созданные юзером в встроенном CAD | `addUserModel({ userModelId, pos })` | | **NpcManager** | NPC и враги (zombies — отдельный `ZombieManager`) | `spawnNpc({ type, pos })` | | **DecoManager** | мелкие воксельные декорации (трава 0.05м) | `paintDeco(pos, brushSize, model)` | | **TerrainManager** | гладкий ландшафт (legacy, voxel-режим) | `paintVoxel(pos, material)` | | **VoxelWorld** + **VoxelRenderer** | новый chunks-based воксельный движок | `setVoxel(x,y,z, type)` | | **FolderManager** | дерево объектов (как Workspace в Roblox) | `createFolder(name, parentId)` | | **GuiManager** | UI игры (HUD, меню) | `setUiElement(id, props)` | | **InventoryManager** | инвентарь игрока (хотбар, рюкзак) | `addItem(itemId, count)` | | **WeaponSystem** | оружие, выстрелы, перезарядка | `equip(weaponId)` / `fire()` | | **ScriptSandbox** | JS-скрипты пользователей (см. §5) | `runScript(scriptCode, gameApi)` | ## 4. Подпапки - **[terrain/](./terrain/)** — legacy воксельный ландшафт (TerrainManager). Кисти, материалы, plant-кисти. Соединён с `BabylonScene` через `TerrainManager.js` в корне. - **[voxel/](./voxel/)** — новый chunks-based движок (16×16×16 чанки + greedy meshing). См. [RUBLOX_VOXEL_ENGINE_PLAN.md](../../../../RUBLOX_VOXEL_ENGINE_PLAN.md). Пока работает параллельно с legacy TerrainManager как shadow-копия для замеров. - **[robloxterrain/](./robloxterrain/)** — Roblox-style визуальные эффекты для гладкого ландшафта (vertex AO, slope-darken, distance fog). - **[types/](./types/)** — общие type-определения и enum'ы (BlockTypes, PrimitiveTypes). ## 5. Скрипты пользователей (sandbox) `ScriptSandbox.js` + `ScriptSandboxWorker.js` — самая опасная часть движка. Тут юзеры пишут JS, который запускается в их браузере при игре. Каждый скрипт запускается **в отдельном Worker** (изоляция: нет доступа к DOM, window, fetch). Общение со сценой — только через `postMessage` и проксированное API `game.*`: ```js // что доступно скрипту: game.player.damage(10) game.player.kill() game.scene.spawn('zombie', { x: 0, y: 0, z: 5 }) game.ui.set('score', 42) game.broadcast('event', payload) // другим скриптам game.onMessage(handler) game.onTick(handler) // ~60 раз/сек ``` Полный список API: [reference_kubikon_scripting_api.md](../../../../disaster-recovery/) (приватный, не публикуется в opensource). **Не звать `game.ui.set` в `onTick` без throttle!** React setState на 60Hz убивает FPS. Дросселировать через 250мс. [feedback_kubikon_ui_set_throttle]. ## 6. Что НЕ трогать (опасные оптимизации) После пары инцидентов задокументировано: - **`scene.blockMaterialDirtyMechanism = true`** — ломает рендер новых мешей (трейсеры пуль, debris). Babylon не пересчитывает uniforms для новых материалов. - **`scene.createOrUpdateSelectionOctree()`** в hot path — O(N²) каждый кадр, лагает на 1000+ мешей. - **`scene.render()` вручную** — Babylon сам в RequestAnimationFrame, дублирование = double render. - **`setInterval(..., 16)`** для игровой логики — используй `scene.onBeforeRenderObservable.add(fn)`, тогда привязано к рендер-циклу. Подробнее в [docs/TUTORIAL_DEBUG_BABYLON.md](../../../docs/TUTORIAL_DEBUG_BABYLON.md). ## 7. Game Runtime — особый режим `GameRuntime.js` — когда юзер жмёт «Play» в редакторе. Это не отдельный код-путь, а **набор флагов** на тех же менеджерах: - `PlayerController.spawn()` — создаёт игрового персонажа (R15-скелет) на spawn-блоке - `PhysicsWorld.enable()` — включается AABB-симуляция (в редакторе отключена) - `ScriptSandbox.startAll()` — запускает все скрипты пользователей - `MultiplayerSync.join()` — если игра опубликована, коннектится к Colyseus-комнате Когда юзер жмёт «Stop» — всё откатывается, состояние из HistoryManager восстанавливается. ## 8. Производительность — ориентиры | Объект | Сколько ок | Сколько начинает лагать | |---|---|---| | Блоки (InstancedMesh) | 50 000 | 200 000+ | | Примитивы (StandardMesh) | 500 | 2000+ | | GLB-модели | 200 | 500+ (зависит от вершин) | | NPC | 50 | 100+ (физика дорогая) | | Активных скриптов | 30 | 100+ (каждый = Worker = поток) | | Частицы | 5000 | 20 000+ | FPS-цель: **60 FPS на средних ноутбуках 2020+**, **30 FPS на школьных машинках 2015+**. ## 9. Что почитать дальше - [terrain/README.md](./terrain/README.md) — воксельный ландшафт - [voxel/README.md](./voxel/README.md) — новый chunks-движок - [../README.md](../README.md) — про папку editor/ выше - [docs/TUTORIAL_ADD_BLOCK.md](../../../docs/TUTORIAL_ADD_BLOCK.md) — добавить новый тип блока - [docs/TUTORIAL_DEBUG_BABYLON.md](../../../docs/TUTORIAL_DEBUG_BABYLON.md) — отладка Babylon-сцены - [docs/TUTORIAL_ADD_SCRIPT_API.md](../../../docs/TUTORIAL_ADD_SCRIPT_API.md) — добавить API в `game.*` Вопросы — в канал `#разработка` на https://team.rublox.pro.