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 — открыть/закрыть чат. Игнорируем когда:
// • уже введён текст в /