fix(studio): кит спавнится на поверхности в фокусе + многочастный в папку + снятие выделения при Play
1) Предметы кита из тулбокса спавнятся на ТВЁРДОЙ поверхности под центром экрана (getPlacementPointAtCenter: raycast в пол/объект), а не под камерой. 2) Многочастный кит (дверь) теперь реально попадает в папку: insertGameplayKit ставил folderId, но дерево не пересобиралось (markDirty не трогает hierarchyDirtyRef) — добавлен hierarchyDirtyRef.current=true. 3) enterPlayMode снимает любое выделение редактора (объект/папка) + убирает пивот папки и gizmo — в Play больше нет подсветки выбранного. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bf93219266
commit
46414d874b
@ -778,12 +778,17 @@ const KubikonEditor = () => {
|
|||||||
const kit = getKit(kitId);
|
const kit = getKit(kitId);
|
||||||
const s = sceneRef.current;
|
const s = sceneRef.current;
|
||||||
if (!kit || !s) return;
|
if (!kit || !s) return;
|
||||||
// Точка вставки — перед камерой редактора (~6м), как у paste.
|
// Точка вставки — на ТВЁРДОЙ поверхности под центром экрана (пол/объект),
|
||||||
let px = 0, pz = 0;
|
// чтобы предмет встал на землю в фокусе камеры, а не висел под камерой.
|
||||||
|
let px = 0, pz = 0, py = 0;
|
||||||
try {
|
try {
|
||||||
|
const gp = s.getPlacementPointAtCenter?.();
|
||||||
|
if (gp) { px = gp.x; pz = gp.z; py = gp.y; }
|
||||||
|
else {
|
||||||
const cam = s.camera;
|
const cam = s.camera;
|
||||||
const fwd = cam?.getForwardRay ? cam.getForwardRay().direction : null;
|
const fwd = cam?.getForwardRay ? cam.getForwardRay().direction : null;
|
||||||
if (cam && fwd) { px = cam.position.x + fwd.x * 6; pz = cam.position.z + fwd.z * 6; }
|
if (cam && fwd) { px = cam.position.x + fwd.x * 6; pz = cam.position.z + fwd.z * 6; }
|
||||||
|
}
|
||||||
} catch (e) { /* ignore */ }
|
} catch (e) { /* ignore */ }
|
||||||
|
|
||||||
// 1) Создаём примитивы кита. Запоминаем все id (первый — для on-target скрипта).
|
// 1) Создаём примитивы кита. Запоминаем все id (первый — для on-target скрипта).
|
||||||
@ -792,7 +797,7 @@ const KubikonEditor = () => {
|
|||||||
if (Array.isArray(kit.prims)) {
|
if (Array.isArray(kit.prims)) {
|
||||||
for (const p of kit.prims) {
|
for (const p of kit.prims) {
|
||||||
const newId = s.primitiveManager?.addInstance(p.type || 'cube', {
|
const newId = s.primitiveManager?.addInstance(p.type || 'cube', {
|
||||||
x: px + (p.x || 0), y: (p.y != null ? p.y : 1), z: pz + (p.z || 0),
|
x: px + (p.x || 0), y: py + (p.y != null ? p.y : 1), z: pz + (p.z || 0),
|
||||||
sx: p.sx, sy: p.sy, sz: p.sz,
|
sx: p.sx, sy: p.sy, sz: p.sz,
|
||||||
color: p.color, material: p.material,
|
color: p.color, material: p.material,
|
||||||
canCollide: p.canCollide !== false, visible: true, anchored: true,
|
canCollide: p.canCollide !== false, visible: true, anchored: true,
|
||||||
@ -829,6 +834,7 @@ const KubikonEditor = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
markDirty();
|
markDirty();
|
||||||
|
hierarchyDirtyRef.current = true; // пересобрать дерево (примитивы с folderId)
|
||||||
setScriptsList(s.getScripts?.() || []);
|
setScriptsList(s.getScripts?.() || []);
|
||||||
if (s.folderManager) setFoldersList(s.folderManager.getAll());
|
if (s.folderManager) setFoldersList(s.folderManager.getAll());
|
||||||
// Выделим созданное и наведём камеру (видно, куда добавилось).
|
// Выделим созданное и наведём камеру (видно, куда добавилось).
|
||||||
|
|||||||
@ -2936,6 +2936,29 @@ export class BabylonScene {
|
|||||||
return { mesh, point: pi.pickedPoint, pickInfo: pi };
|
return { mesh, point: pi.pickedPoint, pickInfo: pi };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Точка под центром экрана на твёрдой поверхности (пол/объект) — для
|
||||||
|
* спавна предметов из тулбокса «в фокусе камеры на земле».
|
||||||
|
* Возвращает { x, y, z } (y — высота поверхности). Fallback: проекция на y=0.
|
||||||
|
*/
|
||||||
|
getPlacementPointAtCenter() {
|
||||||
|
const hit = this._pickFromCenter();
|
||||||
|
if (hit && hit.point) {
|
||||||
|
return { x: hit.point.x, y: hit.point.y, z: hit.point.z };
|
||||||
|
}
|
||||||
|
// Нет попадания — проецируем луч из центра на плоскость y=0.
|
||||||
|
try {
|
||||||
|
const w = this.engine?.getRenderWidth?.() || this.canvas.width;
|
||||||
|
const h = this.engine?.getRenderHeight?.() || this.canvas.height;
|
||||||
|
const ray = this.scene.createPickingRay(w / 2, h / 2, null, this.scene.activeCamera);
|
||||||
|
if (Math.abs(ray.direction.y) > 1e-4) {
|
||||||
|
const t = -ray.origin.y / ray.direction.y;
|
||||||
|
if (t > 0) return { x: ray.origin.x + ray.direction.x * t, y: 0, z: ray.origin.z + ray.direction.z * t };
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Извлечь target {kind, ref} из mesh (proxy/прим/модель).
|
* Извлечь target {kind, ref} из mesh (proxy/прим/модель).
|
||||||
* Используется при клике/touch в Play.
|
* Используется при клике/touch в Play.
|
||||||
@ -5921,6 +5944,13 @@ export class BabylonScene {
|
|||||||
*/
|
*/
|
||||||
enterPlayMode() {
|
enterPlayMode() {
|
||||||
if (this._isPlaying) return;
|
if (this._isPlaying) return;
|
||||||
|
// Снять любое выделение редактора (объект/папка) перед запуском игры —
|
||||||
|
// иначе в Play остаётся подсветка/гизмо выбранного объекта.
|
||||||
|
try {
|
||||||
|
if (this._folderPivot) { this._folderPivot.dispose(); this._folderPivot = null; this._folderPivotId = null; }
|
||||||
|
this._gizmo?.attachTo(null);
|
||||||
|
this.selection?.clear?.();
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
this._isPlaying = true;
|
this._isPlaying = true;
|
||||||
// Сброс состояния касаний — каждый прогон начинается «не касаясь»,
|
// Сброс состояния касаний — каждый прогон начинается «не касаясь»,
|
||||||
// иначе rising-edge touch не сработает, если при стопе игрок стоял на цели.
|
// иначе rising-edge touch не сработает, если при стопе игрок стоял на цели.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user