Add radial pose menu with hotkeys, remove C cycle hotkey
- PlayerPose: Icon (Sprite) + Title (string) UI properties - PoseMenu: modal pause overlay building a radial menu from Icon Button prefab instances; active pose disabled (hover ring marks it); click applies pose via SetPoseIndex and closes - OverviewMenu: pose button wiring with pause hand-off, Esc/Tab chain extended; hotkeys C (toggle pose menu) and V (paint mode) mirroring the buttons incl. identical gating - PlayerPoseController: C cycle hotkey and NextPose() removed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c3d9c06118
commit
3a2e8a7057
5 files changed
+216
-23
No files matched your search
@@ -9,11 +9,12 @@ using UnityEngine.InputSystem;
|
||||
/// The Esc overview menu (the "Overview UI" canvas in Main). Owns ALL Escape handling (Tab acts as an
|
||||
/// alternative to Esc): Esc toggles
|
||||
/// this panel as a modal pause overlay (gameplay suspended via <see cref="GameModeManager.SetPaused"/>);
|
||||
/// while the session browser or match results overlay is open instead, Esc closes that, and while paint
|
||||
/// while the session browser, match results or pose menu 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 /
|
||||
/// match results overlay, 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
|
||||
/// (<see cref="GameModeManager.Modes"/>); the active mode's button is disabled. Hotkeys mirror the
|
||||
/// buttons: C toggles the pose menu, V switches to paint mode (E = taunt lives in GameModeManager). Attach to the
|
||||
/// always-active canvas root; <see cref="panelRoot"/> is the toggled visual child.
|
||||
/// </summary>
|
||||
public class OverviewMenu : MonoBehaviour
|
||||
@@ -29,6 +30,10 @@ public class OverviewMenu : MonoBehaviour
|
||||
"menu stays closed. Empty = found automatically.")]
|
||||
[SerializeField] private MatchResultScreen matchResultScreen;
|
||||
|
||||
[Tooltip("Pose selection menu binder. While its overlay is open it owns Esc (close-only) and this " +
|
||||
"menu stays closed. Empty = found automatically.")]
|
||||
[SerializeField] private PoseMenu poseMenu;
|
||||
|
||||
[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.")]
|
||||
@@ -45,6 +50,8 @@ public class OverviewMenu : MonoBehaviour
|
||||
[Tooltip("Triggers the hider's manual radar ping (same as the E key) and closes the menu. Only shown " +
|
||||
"for a hider during Seek.")]
|
||||
[SerializeField] private Button tauntButton;
|
||||
[Tooltip("Opens the pose selection menu. Only shown while the local player has selectable poses.")]
|
||||
[SerializeField] private Button poseButton;
|
||||
|
||||
private bool _open;
|
||||
|
||||
@@ -53,6 +60,7 @@ public class OverviewMenu : MonoBehaviour
|
||||
if (!gameMode) gameMode = FindFirstObjectByType<GameModeManager>();
|
||||
if (!sessionBrowser) sessionBrowser = FindFirstObjectByType<OnlineSessionBrowserUI>();
|
||||
if (!matchResultScreen) matchResultScreen = FindFirstObjectByType<MatchResultScreen>();
|
||||
if (!poseMenu) poseMenu = FindFirstObjectByType<PoseMenu>();
|
||||
if (panelRoot) panelRoot.SetActive(false);
|
||||
}
|
||||
|
||||
@@ -65,6 +73,7 @@ public class OverviewMenu : MonoBehaviour
|
||||
if (firstPersonButton) firstPersonButton.onClick.AddListener(OnFirstPerson);
|
||||
if (freeCamButton) freeCamButton.onClick.AddListener(OnFreeCam);
|
||||
if (tauntButton) tauntButton.onClick.AddListener(OnTaunt);
|
||||
if (poseButton) poseButton.onClick.AddListener(OnPose);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
@@ -76,18 +85,20 @@ public class OverviewMenu : MonoBehaviour
|
||||
if (firstPersonButton) firstPersonButton.onClick.RemoveListener(OnFirstPerson);
|
||||
if (freeCamButton) freeCamButton.onClick.RemoveListener(OnFreeCam);
|
||||
if (tauntButton) tauntButton.onClick.RemoveListener(OnTaunt);
|
||||
if (poseButton) poseButton.onClick.RemoveListener(OnPose);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
bool browserOpen = sessionBrowser && sessionBrowser.IsOpen;
|
||||
bool resultsOpen = matchResultScreen && matchResultScreen.IsOpen;
|
||||
bool poseOpen = poseMenu && poseMenu.IsOpen;
|
||||
|
||||
if (_open)
|
||||
{
|
||||
// Another overlay took over (results Resolution auto-open) -> hide this panel; the pause
|
||||
// is that overlay's now, so don't release it.
|
||||
if (browserOpen || resultsOpen)
|
||||
if (browserOpen || resultsOpen || poseOpen)
|
||||
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.
|
||||
@@ -95,12 +106,16 @@ public class OverviewMenu : MonoBehaviour
|
||||
HidePanel();
|
||||
}
|
||||
|
||||
if (HandleHotkeys(browserOpen, resultsOpen, poseOpen)) return;
|
||||
|
||||
if (!EscapePressed()) return;
|
||||
|
||||
if (browserOpen)
|
||||
sessionBrowser.Close();
|
||||
else if (resultsOpen)
|
||||
matchResultScreen.Close();
|
||||
else if (poseOpen)
|
||||
poseMenu.Close();
|
||||
else if (_open)
|
||||
Close(resume: true);
|
||||
// Loading (joining a session, or the pre-match Loading barrier): the scene/avatar is mid-setup,
|
||||
@@ -157,6 +172,13 @@ public class OverviewMenu : MonoBehaviour
|
||||
gameMode.SetPaused(false);
|
||||
}
|
||||
|
||||
private void OnPose()
|
||||
{
|
||||
Close(resume: false);
|
||||
if (!(poseMenu && poseMenu.Open()) && gameMode)
|
||||
gameMode.SetPaused(false);
|
||||
}
|
||||
|
||||
private void OnTaunt()
|
||||
{
|
||||
Close(resume: true);
|
||||
@@ -196,6 +218,10 @@ public class OverviewMenu : MonoBehaviour
|
||||
tauntButton.gameObject.SetActive(gameMode.LocalPlayerIsHider && MatchManager.Instance &&
|
||||
MatchManager.Instance.LocalSubPhase == MatchManager.MatchSubPhase.Seek);
|
||||
|
||||
// Pose selection needs a bound local player with poses - hide the button otherwise.
|
||||
if (poseButton)
|
||||
poseButton.gameObject.SetActive(poseMenu && poseMenu.CanOpen);
|
||||
|
||||
SetModeButton<PlayerControlMode>(playerControlButton, modes, active);
|
||||
SetModeButton<PaintMode>(paintButton, modes, active);
|
||||
SetModeButton<FirstPersonMode>(firstPersonButton, modes, active);
|
||||
@@ -213,6 +239,38 @@ public class OverviewMenu : MonoBehaviour
|
||||
button.interactable = !(active is T);
|
||||
}
|
||||
|
||||
/// <summary>Hotkeys mirroring the overlay buttons: C = pose menu (toggle), V = paint mode.
|
||||
/// Same gating as the buttons: inert while another overlay owns input, while loading, and in the
|
||||
/// no-player state. Returns true when a hotkey acted this frame.</summary>
|
||||
private bool HandleHotkeys(bool browserOpen, bool resultsOpen, bool poseOpen)
|
||||
{
|
||||
if (browserOpen || resultsOpen) return false;
|
||||
if (MatchManager.Instance && MatchManager.Instance.IsLoadingLocally) return false;
|
||||
if (!gameMode || gameMode.EffectiveMode == null) return false;
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
bool posePressed = InputUtil.GameplayKeyPressed(Key.C);
|
||||
bool paintPressed = InputUtil.GameplayKeyPressed(Key.V);
|
||||
#else
|
||||
bool posePressed = InputUtil.GameplayKeyPressed(KeyCode.C);
|
||||
bool paintPressed = InputUtil.GameplayKeyPressed(KeyCode.V);
|
||||
#endif
|
||||
|
||||
if (posePressed)
|
||||
{
|
||||
if (poseOpen) poseMenu.Close();
|
||||
else if (poseMenu && poseMenu.CanOpen) OnPose();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (paintPressed && !poseOpen)
|
||||
{
|
||||
SwitchMode<PaintMode>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Esc is NOT TypingGuard-gated: it must work even while a text field has focus.
|
||||
// Tab (alternative binding) IS gated so it stays usable for typing/field navigation.
|
||||
private static bool EscapePressed()
|
||||
|
||||
Reference in new issue
Block a user