fix(studio): многочастные киты в папку + стартовая площадка задаёт спавн

- Кит из нескольких частей (сундук = тело+крышка) теперь кладётся в общую
  папку (folderManager.createFolder + assignToFolder), выделяется как группа.
  Раньше части лежали отдельно в корне.
- Кит «Стартовая площадка»: on-target скрипт телепортирует игрока НА площадку
  в начале игры (game.player.teleport через game.after 0.1с). Теперь игрок
  появляется на ней, а не в фолбэк-точке (0,0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
min 2026-06-05 02:46:25 +03:00
parent 4c8f8c99cb
commit 8b887e866a
2 changed files with 34 additions and 10 deletions

View File

@ -786,8 +786,9 @@ const KubikonEditor = () => {
if (cam && fwd) { px = cam.position.x + fwd.x * 6; pz = cam.position.z + fwd.z * 6; }
} catch (e) { /* ignore */ }
// 1) Создаём примитивы кита. Запоминаем id первого для on-target скрипта.
// 1) Создаём примитивы кита. Запоминаем все id (первый для on-target скрипта).
let firstPrimId = null;
const createdIds = [];
if (Array.isArray(kit.prims)) {
for (const p of kit.prims) {
const newId = s.primitiveManager?.addInstance(p.type || 'cube', {
@ -797,7 +798,19 @@ const KubikonEditor = () => {
canCollide: p.canCollide !== false, visible: true, anchored: true,
name: p.name,
});
if (firstPrimId == null && newId != null) firstPrimId = newId;
if (newId != null) {
createdIds.push(newId);
if (firstPrimId == null) firstPrimId = newId;
}
}
}
// Если кит состоит из НЕСКОЛЬКИХ частей кладём их в общую папку
// (объекты из нескольких частей всегда сгруппированы).
let kitFolderId = null;
if (createdIds.length > 1 && s.folderManager) {
kitFolderId = s.folderManager.createFolder(kit.name);
for (const pid of createdIds) {
s.folderManager.assignToFolder('primitive', pid, kitFolderId);
}
}
@ -817,14 +830,18 @@ const KubikonEditor = () => {
markDirty();
setScriptsList(s.getScripts?.() || []);
// Выделим созданный объект и наведём на него камеру (видно, куда добавилось).
if (firstPrimId != null) {
try {
setActiveTool('select');
if (s.folderManager) setFoldersList(s.folderManager.getAll());
// Выделим созданное и наведём камеру (видно, куда добавилось).
try {
setActiveTool('select');
if (kitFolderId != null) {
s.selection?.selectFolder?.(kitFolderId); // группа из нескольких частей
setGizmoMode('move');
} else if (firstPrimId != null) {
s.selection?.selectPrimitiveById(firstPrimId);
s.focusOnSelection?.();
} catch (e) {}
}
}
s.focusOnSelection?.();
} catch (e) {}
// Тост-уведомление.
try { showToast?.(`Механика «${kit.name}» добавлена`); } catch (e) {}
}, []);

View File

@ -75,9 +75,16 @@ game.onMessage('coins', (m) => { coins += (m && m.add) ? m.add : 1; show(); });`
{
id: 'start-pad',
name: 'Стартовая площадка',
desc: 'Светящаяся платформа-постамент для оформления зоны старта (точка спавна в проекте уже есть по умолчанию).',
desc: 'Светящаяся платформа — игрок появляется НА ней в начале игры (задаёт точку старта).',
icon: 'flag', category: 'world',
prims: [{ type: 'cylinder', x: 0, y: 0.15, z: 0, sx: 3, sy: 0.3, sz: 3, color: '#36d57a', material: 'neon', name: 'Стартовая площадка' }],
scripts: [{ attachTo: 'on-target', code:
`// Игрок появляется на этой площадке в начале игры.
// Небольшая задержка — чтобы позиция объекта и игрок успели проинициализироваться.
game.after(0.1, () => {
const p = game.self.position;
game.player.teleport(p.x, p.y + 1.5, p.z);
});` }],
},
{
id: 'checkpoint',