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

Баг: после удаления точки спавна она всё равно появлялась при запуске.
Теперь _spawnEnabled синхронизируется в React (spawnEnabledUI) через
onSceneChange/load/setSpawn/deleteSpawn; пункт «Точка спавна» скрыт из дерева
когда удалён; player.start использует фолбэк (0,поверхность+2,0). Стартовая
площадка теперь срабатывает (игрок не телепортируется на старый спавн).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
min 2026-06-05 02:33:15 +03:00
parent 471af1cdeb
commit 7fc4ee94f6
2 changed files with 11 additions and 2 deletions

View File

@ -242,7 +242,7 @@ const HierarchyPanel = ({
onSelectSound, onSelectPlayer, onSelectPlayerProps, onSelectFloor,
guiElements = [], onSelectGui, onCreateGui, onDeleteGui, onRenameGui, onMoveGuiZ, onSetGuiParent,
guiOverlayHidden = false, onToggleGuiOverlay,
floorEnabled = true, onCreateFloor, onDeleteFloor, onDeleteSpawn,
floorEnabled = true, onCreateFloor, onDeleteFloor, onDeleteSpawn, spawnEnabled = true,
scripts = [], onSelectScript, onCreateScript, onDeleteScript,
onRenameModel, onRenamePrimitive, onRenameScript,
/**
@ -781,7 +781,8 @@ const HierarchyPanel = ({
/>
{workspaceOpen && (
<div style={{ paddingLeft: 8 }}>
{/* Точка спавна — кликабельная */}
{/* Точка спавна — кликабельная. Скрыта если удалена. */}
{spawnEnabled !== false && (
<div
className={`${cl.item} ${selection?.type === 'spawn' ? cl.itemSelected : ''}`}
onClick={() => onSelectSpawn?.()}
@ -792,6 +793,7 @@ const HierarchyPanel = ({
<span className={cl.itemIcon} style={{ display: 'inline-flex' }}><Icon name="flag" size={14} /></span>
<span className={cl.itemLabel}>Точка спавна</span>
</div>
)}
{/* Пол — псевдо-объект, если включён */}
{floorEnabled && (

View File

@ -606,6 +606,7 @@ const KubikonEditor = () => {
const [crosshair, setCrosshairUI] = useState('none');
// Видимость пола в иерархии
const [floorEnabled, setFloorEnabledUI] = useState(true);
const [spawnEnabledUI, setSpawnEnabledUI] = useState(true);
// Табы над viewport (Roblox-style): «🎬 Сцена» + открытые скрипты
const [openTabs, setOpenTabs] = useState([{ id: 'scene', kind: 'scene', title: 'Сцена' }]);
const [activeTabId, setActiveTabId] = useState('scene');
@ -1354,6 +1355,8 @@ const KubikonEditor = () => {
markDirty();
// Иерархия изменилась interval пересоберёт списки на след. тике.
hierarchyDirtyRef.current = true;
// Синк флага точки спавна (например после Delete-клавиши).
try { setSpawnEnabledUI(scene.hasSpawn?.() !== false); } catch (e) {}
});
// Этап 5: подключаем API пользовательских моделей в BabylonScene,
@ -1558,6 +1561,7 @@ const KubikonEditor = () => {
const ch = sceneRef.current.getCrosshair?.();
if (ch) setCrosshairUI(ch);
setFloorEnabledUI(sceneRef.current.isFloorEnabled?.() !== false);
setSpawnEnabledUI(sceneRef.current.hasSpawn?.() !== false);
const a = sceneRef.current.getAudioState?.();
if (a?.ambientId) setAmbientIdUI(a.ambientId);
if (a?.musicId) setMusicIdUI(a.musicId);
@ -2112,6 +2116,7 @@ const KubikonEditor = () => {
onPlayToggle={handlePlay}
onSetSpawn={() => {
sceneRef.current?.setSpawnAtCamera();
setSpawnEnabledUI(true);
}}
hasSelection={!!selection}
onDuplicate={() => sceneRef.current?.duplicateSelected()}
@ -3245,6 +3250,7 @@ const KubikonEditor = () => {
setActiveTool('select');
}}
floorEnabled={floorEnabled}
spawnEnabled={spawnEnabledUI}
onSelectFloor={() => {
sceneRef.current?.selection?.selectFloor?.();
setActiveTool('select');
@ -3312,6 +3318,7 @@ const KubikonEditor = () => {
onDeleteSpawn={() => {
sceneRef.current?.deleteSpawn?.();
sceneRef.current?.clearSelection?.();
setSpawnEnabledUI(false);
markDirty();
}}
onSelectLighting={() => {