Add OverviewMenu escape menu, remove Tab mode cycling
- New OverviewMenu component: Esc toggles the pause overlay, buttons switch modes via GameModeManager.TrySwitchTo<T>() and close the panel; visibility follows the role-scoped mode set, active mode disabled. - Esc in paint mode exits to camera mode instead (PlayerControlMode, hunter falls back to FirstPersonMode) rather than opening the menu. - Centralized all Esc handling in OverviewMenu (browser close included) to avoid frame races; OnlineSessionBrowserUI gains IsOpen/Open/Close. - GameModeManager: switchKey Tab-cycle removed, TrySwitchTo<T> added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
367bc9cee0
commit
7ac6038121
4 files changed
+239
-53
No files matched your search
@@ -0,0 +1,180 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The Esc overview menu (the "Overview UI" canvas in Main). Owns ALL Escape handling: Esc toggles
|
||||
/// this panel as a modal pause overlay (gameplay suspended via <see cref="GameModeManager.SetPaused"/>);
|
||||
/// while the session browser overlay is open instead, Esc closes that, and while paint mode is active
|
||||
/// Esc exits to the camera mode (Player Control, or First Person for the hunter). Buttons switch game modes
|
||||
/// (via <see cref="GameModeManager.TrySwitchTo{T}"/>) or open the session browser, and every click
|
||||
/// closes the panel. Mode buttons are only shown for modes present in the current role-scoped set
|
||||
/// (<see cref="GameModeManager.Modes"/>); the active mode's button is disabled. Attach to the
|
||||
/// always-active canvas root; <see cref="panelRoot"/> is the toggled visual child.
|
||||
/// </summary>
|
||||
public class OverviewMenu : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Mode manager driving pause + mode switches. Empty = found automatically.")]
|
||||
[SerializeField] private GameModeManager gameMode;
|
||||
|
||||
[Tooltip("Session browser binder. While its overlay is open it owns Esc (close-only) and this menu " +
|
||||
"stays closed. Empty = found automatically.")]
|
||||
[SerializeField] private OnlineSessionBrowserUI sessionBrowser;
|
||||
|
||||
[Header("Panel")]
|
||||
[Tooltip("Visual container toggled by Esc. Must be a CHILD of this binder's object so the binder " +
|
||||
"keeps reading Esc while the panel is hidden.")]
|
||||
[SerializeField] private GameObject panelRoot;
|
||||
|
||||
[Header("Buttons")]
|
||||
[SerializeField] private Button sessionButton;
|
||||
[Tooltip("Enters Player Control (third-person orbit) mode.")]
|
||||
[SerializeField] private Button playerControlButton;
|
||||
[SerializeField] private Button paintButton;
|
||||
[SerializeField] private Button firstPersonButton;
|
||||
[SerializeField] private Button freeCamButton;
|
||||
|
||||
private bool _open;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (!gameMode) gameMode = FindFirstObjectByType<GameModeManager>();
|
||||
if (!sessionBrowser) sessionBrowser = FindFirstObjectByType<OnlineSessionBrowserUI>();
|
||||
if (panelRoot) panelRoot.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (sessionButton) sessionButton.onClick.AddListener(OnSession);
|
||||
if (playerControlButton) playerControlButton.onClick.AddListener(OnPlayerControl);
|
||||
if (paintButton) paintButton.onClick.AddListener(OnPaint);
|
||||
if (firstPersonButton) firstPersonButton.onClick.AddListener(OnFirstPerson);
|
||||
if (freeCamButton) freeCamButton.onClick.AddListener(OnFreeCam);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (sessionButton) sessionButton.onClick.RemoveListener(OnSession);
|
||||
if (playerControlButton) playerControlButton.onClick.RemoveListener(OnPlayerControl);
|
||||
if (paintButton) paintButton.onClick.RemoveListener(OnPaint);
|
||||
if (firstPersonButton) firstPersonButton.onClick.RemoveListener(OnFirstPerson);
|
||||
if (freeCamButton) freeCamButton.onClick.RemoveListener(OnFreeCam);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
bool browserOpen = sessionBrowser && sessionBrowser.IsOpen;
|
||||
|
||||
if (_open)
|
||||
{
|
||||
// The browser overlay took over (its Resolution auto-open) -> hide this panel; the pause
|
||||
// is the browser's now, so don't release it.
|
||||
if (browserOpen)
|
||||
HidePanel();
|
||||
// An external state reset (respawn/bind, disconnect) dropped the pause underneath the
|
||||
// open panel -> don't linger as a stale overlay over live gameplay.
|
||||
else if (gameMode && !gameMode.IsPaused)
|
||||
HidePanel();
|
||||
}
|
||||
|
||||
if (!EscapePressed()) return;
|
||||
|
||||
if (browserOpen)
|
||||
sessionBrowser.Close();
|
||||
else if (_open)
|
||||
Close(resume: true);
|
||||
// Paint mode: Esc exits back to the camera mode instead of opening the menu. Player Control
|
||||
// first; the hunter's role-scoped set has none, so it falls through to First Person.
|
||||
else if (gameMode && gameMode.EffectiveMode is PaintMode)
|
||||
{
|
||||
if (!gameMode.TrySwitchTo<PlayerControlMode>() && !gameMode.TrySwitchTo<FirstPersonMode>())
|
||||
Open();
|
||||
}
|
||||
// No active/paused mode = the inert no-player state (pre-lobby Main, where the session panel
|
||||
// is always shown) - nothing to pause, don't open.
|
||||
else if (gameMode && gameMode.EffectiveMode != null)
|
||||
Open();
|
||||
}
|
||||
|
||||
private void Open()
|
||||
{
|
||||
_open = true;
|
||||
if (panelRoot) panelRoot.SetActive(true);
|
||||
RefreshButtons();
|
||||
if (gameMode) gameMode.SetPaused(true);
|
||||
}
|
||||
|
||||
/// <summary>Hides the panel; <paramref name="resume"/> = false hands the pause over (session
|
||||
/// browser opening) instead of resuming gameplay.</summary>
|
||||
private void Close(bool resume)
|
||||
{
|
||||
HidePanel();
|
||||
if (resume && gameMode) gameMode.SetPaused(false);
|
||||
}
|
||||
|
||||
private void HidePanel()
|
||||
{
|
||||
_open = false;
|
||||
if (panelRoot) panelRoot.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnSession()
|
||||
{
|
||||
Close(resume: false);
|
||||
// Not connected (defensive - the menu should not be openable then): don't strand the pause.
|
||||
if (!(sessionBrowser && sessionBrowser.Open()) && gameMode)
|
||||
gameMode.SetPaused(false);
|
||||
}
|
||||
|
||||
private void OnPlayerControl() => SwitchMode<PlayerControlMode>();
|
||||
private void OnPaint() => SwitchMode<PaintMode>();
|
||||
private void OnFirstPerson() => SwitchMode<FirstPersonMode>();
|
||||
private void OnFreeCam() => SwitchMode<FreeCamMode>();
|
||||
|
||||
private void SwitchMode<T>() where T : GameMode
|
||||
{
|
||||
// Resume first so the switch runs on a live cycle (SwitchTo on a paused manager would fight
|
||||
// the pause bookkeeping); picking the already-active mode is a no-op inside SwitchTo.
|
||||
Close(resume: true);
|
||||
if (gameMode) gameMode.TrySwitchTo<T>();
|
||||
}
|
||||
|
||||
/// <summary>Shows only the buttons whose mode exists in the current role-scoped set and disables
|
||||
/// the active mode's button. Ran on every open - the set only changes with a (re)bind, which
|
||||
/// force-closes the panel anyway.</summary>
|
||||
private void RefreshButtons()
|
||||
{
|
||||
if (!gameMode) return;
|
||||
IReadOnlyList<GameMode> modes = gameMode.Modes;
|
||||
GameMode active = gameMode.EffectiveMode;
|
||||
|
||||
SetModeButton<PlayerControlMode>(playerControlButton, modes, active);
|
||||
SetModeButton<PaintMode>(paintButton, modes, active);
|
||||
SetModeButton<FirstPersonMode>(firstPersonButton, modes, active);
|
||||
SetModeButton<FreeCamMode>(freeCamButton, modes, active);
|
||||
}
|
||||
|
||||
private static void SetModeButton<T>(Button button, IReadOnlyList<GameMode> modes, GameMode active)
|
||||
where T : GameMode
|
||||
{
|
||||
if (!button) return;
|
||||
bool present = false;
|
||||
for (int i = 0; i < modes.Count; i++)
|
||||
if (modes[i] is T) { present = true; break; }
|
||||
button.gameObject.SetActive(present);
|
||||
button.interactable = !(active is T);
|
||||
}
|
||||
|
||||
// NOT TypingGuard-gated (GameplayKeyPressed): Esc must work even while a text field has focus.
|
||||
private static bool EscapePressed()
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
return InputUtil.KeyPressedThisFrame(Key.Escape);
|
||||
#else
|
||||
return InputUtil.KeyPressedThisFrame(KeyCode.Escape);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user