diff --git a/src/KubikonPlayer/KubikonPlayer.jsx b/src/KubikonPlayer/KubikonPlayer.jsx index 7ac0043..9d62987 100644 --- a/src/KubikonPlayer/KubikonPlayer.jsx +++ b/src/KubikonPlayer/KubikonPlayer.jsx @@ -517,13 +517,20 @@ const KubikonPlayer = () => { s?.player?.setUiCursorMode?.(true); setChatOpen(false); setTopMenuOpen(true); + try { if (s) s._playerMenuOpen = true; } catch (e) { /* ignore */ } } }); - // ESC в Play → меню-оверлей поверх ЖИВОЙ игры (Roblox-style). Play не - // прерывается, скрипты продолжают идти, игрок не респавнится. - scene.setOnEscMenu?.(() => { - setChatOpen(false); - setTopMenuOpen(true); + // ESC в Play → TOGGLE меню-оверлея поверх ЖИВОЙ игры (Roblox-style). + // Движок сам решает open/close (единый источник истины _playerMenuOpen) + // и передаёт сюда. Это убирает гонку двух ESC-обработчиков, из-за которой + // меню открывалось поверх меню, а orbit-камера по ПКМ зависала. + scene.setOnEscMenu?.((open) => { + if (open) { + setChatOpen(false); + setTopMenuOpen(true); + } else { + setTopMenuOpen(false); + } }); // Загружаем проект. @@ -734,26 +741,20 @@ const KubikonPlayer = () => { p._uiCursorMode = true; setChatOpen(false); setTopMenuOpen(true); + // Синхронизируем единый флаг меню в движке, чтобы следующий ESC + // сработал как toggle-закрытие (а не открыл второе меню). + try { s._playerMenuOpen = true; } catch (e) { /* ignore */ } }; // capture-фаза, чтобы успеть раньше PlayerController document.addEventListener('pointerlockchange', onLockChange, true); return () => document.removeEventListener('pointerlockchange', onLockChange, true); }, []); - // Повторный ESC (когда меню уже открыто) — закрыть меню и вернуть - // мышь в игру. - useEffect(() => { - if (!topMenuOpen) return; - const onEsc = (e) => { - if (e.key !== 'Escape') return; - const s = sceneRef.current; - if (!s || !s._isPlaying) return; - setTopMenuOpen(false); - s.player?.setUiCursorMode?.(false); - }; - window.addEventListener('keydown', onEsc, true); - return () => window.removeEventListener('keydown', onEsc, true); - }, [topMenuOpen]); + // Повторный ESC (toggle закрытие) теперь обрабатывает движок через + // setOnExitRequest → _onEscMenu(false). Отдельный React-обработчик ESC + // УБРАН — он слушал тот же ESC, что и движок, и создавал гонку: + // меню открывалось поверх себя, а _uiCursorMode застревал в true + // (orbit-камера по ПКМ переставала работать после закрытия меню). // Горячая клавиша T — открыть/закрыть чат. Игнорируем когда: // • уже введён текст в /