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 s = sceneRef.current;
|
||||
if (!kit || !s) return;
|
||||
// Точка вставки — перед камерой редактора (~6м), как у paste.
|
||||
let px = 0, pz = 0;
|
||||
// Точка вставки — на ТВЁРДОЙ поверхности под центром экрана (пол/объект),
|
||||
// чтобы предмет встал на землю в фокусе камеры, а не висел под камерой.
|
||||
let px = 0, pz = 0, py = 0;
|
||||
try {
|
||||
const gp = s.getPlacementPointAtCenter?.();
|
||||
if (gp) { px = gp.x; pz = gp.z; py = gp.y; }
|
||||
else {
|
||||
const cam = s.camera;
|
||||
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; }
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
// 1) Создаём примитивы кита. Запоминаем все id (первый — для on-target скрипта).
|
||||
@ -792,7 +797,7 @@ const KubikonEditor = () => {
|
||||
if (Array.isArray(kit.prims)) {
|
||||
for (const p of kit.prims) {
|
||||
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,
|
||||
color: p.color, material: p.material,
|
||||
canCollide: p.canCollide !== false, visible: true, anchored: true,
|
||||
@ -829,6 +834,7 @@ const KubikonEditor = () => {
|
||||
}
|
||||
|
||||
markDirty();
|
||||
hierarchyDirtyRef.current = true; // пересобрать дерево (примитивы с folderId)
|
||||
setScriptsList(s.getScripts?.() || []);
|
||||
if (s.folderManager) setFoldersList(s.folderManager.getAll());
|
||||
// Выделим созданное и наведём камеру (видно, куда добавилось).
|
||||
|
||||
@ -2936,6 +2936,29 @@ export class BabylonScene {
|
||||
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/прим/модель).
|
||||
* Используется при клике/touch в Play.
|
||||
@ -5921,6 +5944,13 @@ export class BabylonScene {
|
||||
*/
|
||||
enterPlayMode() {
|
||||
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;
|
||||
// Сброс состояния касаний — каждый прогон начинается «не касаясь»,
|
||||
// иначе rising-edge touch не сработает, если при стопе игрок стоял на цели.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user