781 lines
26 KiB
C#
781 lines
26 KiB
C#
using UnityEngine;
|
|
using System;
|
|
using System.Collections;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEngine.UI;
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Central gameplay pause manager.
|
|
/// - ESC / top-right Pause button can pause only in gameplay scene.
|
|
/// - Continue: hide pause panel, replay 3-2-1-Go, then resume chart + music.
|
|
/// - Replay: reload current gameplay scene (with current selected song/difficulty if available).
|
|
/// - Exit: return to selectYourSongFirst.
|
|
/// </summary>
|
|
public class PauseManager : MonoBehaviour
|
|
{
|
|
public static PauseManager Instance { get; private set; }
|
|
|
|
[Header("Overlay")]
|
|
public GameObject overlayRoot;
|
|
[SerializeField] private float overlayFadeDuration = 0.2f;
|
|
[SerializeField] private bool preferPausePrefabOverWhiteScreen = true;
|
|
[SerializeField] private string legacyWhiteScreenRootName = "pauseWhiteScreen";
|
|
|
|
[Header("Gameplay Scope")]
|
|
[SerializeField] private bool onlyInGameplayScene = true;
|
|
[SerializeField] private string gameplaySceneName = "gamePlay_gamePlay";
|
|
[SerializeField] private bool requirePlaybackStarted = true;
|
|
|
|
[Header("Buttons")]
|
|
[SerializeField] private string pauseButtonScenePath = "artworks/NEW UI/zhuangshi (3)/Pause";
|
|
[SerializeField] private Button pauseTriggerButton;
|
|
[SerializeField] private Button continueButton;
|
|
[SerializeField] private Button replayButton;
|
|
[SerializeField] private Button exitButton;
|
|
[SerializeField] private Button settingsButton;
|
|
|
|
[Header("Settings Prefab")]
|
|
[SerializeField] private GameObject settingsPrefab;
|
|
|
|
[Header("Pause Entry Animation")]
|
|
[SerializeField] private bool playPauseEntryAnimation = true;
|
|
[SerializeField] private float pauseItemEnterDuration = 0.18f;
|
|
[SerializeField] private float pauseItemEnterStagger = 0.016f;
|
|
|
|
public bool IsPaused { get; private set; } = false;
|
|
public event Action<bool> OnPauseStateChanged;
|
|
|
|
private CanvasGroup overlayCanvasGroup;
|
|
private RectTransform overlayPanelRoot;
|
|
private CanvasGroup overlayPanelCanvasGroup;
|
|
private Vector3 panelShownScale = Vector3.zero;
|
|
private static readonly Vector3 PanelHiddenScale = Vector3.zero;
|
|
private Coroutine overlayFadeCoroutine;
|
|
private Coroutine continueCoroutine;
|
|
private bool buttonsBound = false;
|
|
private float nextBindRetryTime = 0f;
|
|
|
|
private const string ExitSceneName = "selectYourSongFirst";
|
|
|
|
void Awake()
|
|
{
|
|
if (Instance == null) Instance = this;
|
|
else
|
|
{
|
|
Destroy(gameObject);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
ResolveOverlayRootForPausePrefab();
|
|
EnsureOverlayCanvasGroup();
|
|
EnsureOverlayPanelRoot();
|
|
|
|
// Ensure CanvasGroup is initialized to 0 alpha, but keep interactable/blocksRaycasts true
|
|
if (overlayCanvasGroup != null)
|
|
{
|
|
overlayCanvasGroup.alpha = 0f;
|
|
overlayCanvasGroup.interactable = true;
|
|
overlayCanvasGroup.blocksRaycasts = true;
|
|
}
|
|
|
|
// Initialize buttons but DO NOT call SetOverlayVisibleImmediate(false) here
|
|
// because it would reset the flags we just set.
|
|
// Instead, we manually ensure the object is active if that's the desired initial state,
|
|
// or just rely on the alpha=0 to hide it.
|
|
// Assuming user wants the object active but transparent:
|
|
if (overlayRoot != null) overlayRoot.SetActive(true);
|
|
|
|
TryBindButtons(force: true);
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (!buttonsBound && Time.unscaledTime >= nextBindRetryTime)
|
|
{
|
|
TryBindButtons(force: false);
|
|
}
|
|
|
|
if (Input.GetKeyDown(KeyCode.Delete))
|
|
{
|
|
#if UNITY_EDITOR
|
|
EditorApplication.isPlaying = false;
|
|
#else
|
|
Application.Quit();
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (Input.GetKeyDown(KeyCode.Escape))
|
|
{
|
|
// User requested ESC as pause trigger in gameplay only.
|
|
if (CanPauseFromInput())
|
|
{
|
|
if (!IsPaused)
|
|
{
|
|
Pause(true);
|
|
}
|
|
else
|
|
{
|
|
Pause(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Keep existing Backspace quick-exit while paused.
|
|
if (IsPaused && Input.GetKeyDown(KeyCode.Backspace))
|
|
{
|
|
Pause(false);
|
|
StartCoroutine(LoadSceneAsync("Main_main"));
|
|
}
|
|
}
|
|
|
|
public void Pause(bool pause)
|
|
{
|
|
if (pause)
|
|
{
|
|
if (IsPaused) return;
|
|
IsPaused = true;
|
|
Time.timeScale = 0f;
|
|
OnPauseStateChanged?.Invoke(true);
|
|
|
|
// Startup flows call Pause(true) before real gameplay starts.
|
|
// In that phase we keep time paused but do not show pause UI.
|
|
|
|
// Calculate allowOverlay WITHOUT checking IsPaused (since we just set it to true).
|
|
bool allowOverlay = true;
|
|
if (onlyInGameplayScene && !IsGameplaySceneActive()) allowOverlay = false;
|
|
else if (requirePlaybackStarted)
|
|
{
|
|
var gm = FindAnyObjectByType<GameManager>();
|
|
// If GM missing or playback not started, don't show overlay yet
|
|
if (gm != null && !gm.PlaybackStarted) allowOverlay = false;
|
|
}
|
|
|
|
if (allowOverlay)
|
|
{
|
|
ShowOverlayAnimated(true);
|
|
}
|
|
else
|
|
{
|
|
// Startup Pause State:
|
|
// Keep object active and blocking raycasts (per user request), but invisible.
|
|
if (overlayRoot != null) overlayRoot.SetActive(true);
|
|
|
|
if (overlayCanvasGroup != null)
|
|
{
|
|
overlayCanvasGroup.alpha = 0f;
|
|
overlayCanvasGroup.interactable = true;
|
|
overlayCanvasGroup.blocksRaycasts = true;
|
|
}
|
|
|
|
if (overlayPanelCanvasGroup != null)
|
|
{
|
|
overlayPanelCanvasGroup.alpha = 0f;
|
|
overlayPanelCanvasGroup.interactable = true;
|
|
overlayPanelCanvasGroup.blocksRaycasts = true;
|
|
}
|
|
|
|
if (overlayPanelRoot != null)
|
|
overlayPanelRoot.localScale = PanelHiddenScale;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!IsPaused) return;
|
|
IsPaused = false;
|
|
Time.timeScale = 1f;
|
|
OnPauseStateChanged?.Invoke(false);
|
|
ShowOverlayAnimated(false);
|
|
}
|
|
}
|
|
|
|
public void TogglePause()
|
|
{
|
|
if (!CanPauseFromInput()) return;
|
|
if (!IsPaused) Pause(true);
|
|
}
|
|
|
|
public void ContinueFromPause()
|
|
{
|
|
if (!IsPaused) return;
|
|
if (continueCoroutine != null) return;
|
|
continueCoroutine = StartCoroutine(ContinueFromPauseCoroutine());
|
|
}
|
|
|
|
public void ReplayFromPause()
|
|
{
|
|
if (!IsPaused) return;
|
|
if (continueCoroutine != null)
|
|
{
|
|
StopCoroutine(continueCoroutine);
|
|
continueCoroutine = null;
|
|
}
|
|
StartCoroutine(ReplayFromPauseCoroutine());
|
|
}
|
|
|
|
public void ExitFromPause()
|
|
{
|
|
if (!IsPaused) return;
|
|
if (continueCoroutine != null)
|
|
{
|
|
StopCoroutine(continueCoroutine);
|
|
continueCoroutine = null;
|
|
}
|
|
StartCoroutine(ExitFromPauseCoroutine());
|
|
}
|
|
|
|
private bool IsGameplaySceneActive()
|
|
{
|
|
string sceneName = SceneManager.GetActiveScene().name ?? string.Empty;
|
|
if (string.Equals(sceneName, gameplaySceneName, StringComparison.OrdinalIgnoreCase)) return true;
|
|
if (sceneName.IndexOf("gameplay", StringComparison.OrdinalIgnoreCase) >= 0) return true;
|
|
return false;
|
|
}
|
|
|
|
private bool CanPauseFromInput()
|
|
{
|
|
if (onlyInGameplayScene && !IsGameplaySceneActive()) return false;
|
|
if (!requirePlaybackStarted) return true;
|
|
|
|
var gm = FindAnyObjectByType<GameManager>();
|
|
if (gm == null) return false;
|
|
|
|
// If already paused, allow input to unpause regardless of PlaybackStarted state
|
|
if (IsPaused) return true;
|
|
|
|
return gm.PlaybackStarted;
|
|
}
|
|
|
|
private void EnsureOverlayCanvasGroup()
|
|
{
|
|
if (overlayRoot == null) return;
|
|
|
|
// Priority: Use existing CanvasGroup if available (Index first)
|
|
if (!overlayRoot.TryGetComponent(out overlayCanvasGroup))
|
|
{
|
|
// Fallback: Create only if not found
|
|
overlayCanvasGroup = overlayRoot.AddComponent<CanvasGroup>();
|
|
}
|
|
}
|
|
|
|
private void ResolveOverlayRootForPausePrefab()
|
|
{
|
|
// Priority: If manually assigned in Inspector, respect it.
|
|
if (overlayRoot != null) return;
|
|
|
|
if (!preferPausePrefabOverWhiteScreen) return;
|
|
// Prefer the real pause prefab root (contains Continue/Replay/Exit).
|
|
GameObject pauseRoot = FindPauseMenuRootInScene();
|
|
if (pauseRoot != null)
|
|
{
|
|
overlayRoot = pauseRoot;
|
|
return;
|
|
}
|
|
|
|
if (overlayRoot == null) return;
|
|
|
|
// Legacy setup fallback: PauseManager points to pauseWhiteScreen.
|
|
if (!string.Equals(overlayRoot.name, legacyWhiteScreenRootName, StringComparison.OrdinalIgnoreCase))
|
|
return;
|
|
|
|
GameObject candidate = FindPauseOverlayCandidate(overlayRoot.transform);
|
|
if (candidate == null)
|
|
candidate = FindPauseOverlayCandidate(null);
|
|
if (candidate != null && candidate != overlayRoot)
|
|
overlayRoot = candidate;
|
|
}
|
|
|
|
private GameObject FindPauseOverlayCandidate(Transform searchRoot)
|
|
{
|
|
Canvas[] canvases = searchRoot != null
|
|
? searchRoot.GetComponentsInChildren<Canvas>(true)
|
|
: FindObjectsByType<Canvas>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
|
|
|
for (int i = 0; i < canvases.Length; i++)
|
|
{
|
|
Canvas canvas = canvases[i];
|
|
if (canvas == null) continue;
|
|
|
|
GameObject go = canvas.gameObject;
|
|
if (go == null || go == overlayRoot) continue;
|
|
if (!HasPauseButtons(go)) continue;
|
|
return go;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private GameObject FindPauseMenuRootInScene()
|
|
{
|
|
var all = Resources.FindObjectsOfTypeAll<Transform>();
|
|
for (int i = 0; i < all.Length; i++)
|
|
{
|
|
var t = all[i];
|
|
if (t == null) continue;
|
|
if (!t.gameObject.scene.IsValid()) continue;
|
|
if (!string.Equals(t.name, "pause", StringComparison.OrdinalIgnoreCase)) continue;
|
|
|
|
GameObject go = t.gameObject;
|
|
if (HasPauseButtons(go))
|
|
return go;
|
|
}
|
|
|
|
// Fallback: any object that contains all three pause action buttons.
|
|
for (int i = 0; i < all.Length; i++)
|
|
{
|
|
var t = all[i];
|
|
if (t == null) continue;
|
|
if (!t.gameObject.scene.IsValid()) continue;
|
|
GameObject go = t.gameObject;
|
|
if (HasPauseButtons(go))
|
|
return go;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static bool HasPauseButtons(GameObject root)
|
|
{
|
|
if (root == null) return false;
|
|
Button[] buttons = root.GetComponentsInChildren<Button>(true);
|
|
bool hasContinue = false;
|
|
bool hasReplay = false;
|
|
bool hasExit = false;
|
|
|
|
for (int i = 0; i < buttons.Length; i++)
|
|
{
|
|
Button b = buttons[i];
|
|
if (b == null) continue;
|
|
string n = b.gameObject.name;
|
|
if (string.Equals(n, "Continue", StringComparison.OrdinalIgnoreCase)) hasContinue = true;
|
|
else if (string.Equals(n, "Replay", StringComparison.OrdinalIgnoreCase)) hasReplay = true;
|
|
else if (string.Equals(n, "Exit", StringComparison.OrdinalIgnoreCase)) hasExit = true;
|
|
}
|
|
|
|
return hasContinue && hasReplay && hasExit;
|
|
}
|
|
|
|
private void EnsureOverlayPanelRoot()
|
|
{
|
|
if (overlayRoot == null) return;
|
|
|
|
if (overlayPanelRoot == null)
|
|
{
|
|
var canvases = overlayRoot.GetComponentsInChildren<Canvas>(true);
|
|
for (int i = 0; i < canvases.Length; i++)
|
|
{
|
|
var canvas = canvases[i];
|
|
if (canvas == null) continue;
|
|
if (canvas.gameObject == overlayRoot) continue;
|
|
|
|
overlayPanelRoot = canvas.GetComponent<RectTransform>();
|
|
if (overlayPanelRoot != null)
|
|
break;
|
|
}
|
|
|
|
if (overlayPanelRoot == null)
|
|
{
|
|
var rts = overlayRoot.GetComponentsInChildren<RectTransform>(true);
|
|
for (int i = 0; i < rts.Length; i++)
|
|
{
|
|
var rt = rts[i];
|
|
if (rt == null) continue;
|
|
if (rt.gameObject == overlayRoot) continue;
|
|
|
|
overlayPanelRoot = rt;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (overlayPanelRoot == null) return;
|
|
|
|
if (overlayPanelCanvasGroup == null)
|
|
{
|
|
overlayPanelCanvasGroup = overlayPanelRoot.GetComponent<CanvasGroup>();
|
|
if (overlayPanelCanvasGroup == null)
|
|
overlayPanelCanvasGroup = overlayPanelRoot.gameObject.AddComponent<CanvasGroup>();
|
|
}
|
|
|
|
if (IsScaleNearlyZero(panelShownScale))
|
|
{
|
|
Vector3 current = overlayPanelRoot.localScale;
|
|
panelShownScale = IsScaleNearlyZero(current) ? Vector3.one : current;
|
|
}
|
|
}
|
|
|
|
private static bool IsScaleNearlyZero(Vector3 scale)
|
|
{
|
|
return Mathf.Abs(scale.x) <= 0.0001f
|
|
&& Mathf.Abs(scale.y) <= 0.0001f
|
|
&& Mathf.Abs(scale.z) <= 0.0001f;
|
|
}
|
|
|
|
private void SetOverlayVisibleImmediate(bool visible)
|
|
{
|
|
if (overlayRoot == null) return;
|
|
EnsureOverlayCanvasGroup();
|
|
EnsureOverlayPanelRoot();
|
|
|
|
overlayRoot.SetActive(visible);
|
|
if (overlayCanvasGroup != null)
|
|
{
|
|
overlayCanvasGroup.alpha = visible ? 1f : 0f;
|
|
overlayCanvasGroup.interactable = visible;
|
|
overlayCanvasGroup.blocksRaycasts = visible;
|
|
}
|
|
|
|
if (overlayPanelCanvasGroup != null)
|
|
{
|
|
overlayPanelCanvasGroup.alpha = visible ? 1f : 0f;
|
|
overlayPanelCanvasGroup.interactable = visible;
|
|
overlayPanelCanvasGroup.blocksRaycasts = visible;
|
|
}
|
|
|
|
if (overlayPanelRoot != null)
|
|
{
|
|
overlayPanelRoot.localScale = visible ? panelShownScale : PanelHiddenScale;
|
|
}
|
|
}
|
|
|
|
private void ShowOverlayAnimated(bool show)
|
|
{
|
|
if (overlayRoot == null)
|
|
return;
|
|
|
|
EnsureOverlayCanvasGroup();
|
|
if (overlayFadeCoroutine != null)
|
|
{
|
|
StopCoroutine(overlayFadeCoroutine);
|
|
overlayFadeCoroutine = null;
|
|
}
|
|
overlayFadeCoroutine = StartCoroutine(FadeOverlayCoroutine(show));
|
|
}
|
|
|
|
private IEnumerator FadeOverlayCoroutine(bool show)
|
|
{
|
|
if (overlayRoot == null)
|
|
yield break;
|
|
|
|
EnsureOverlayCanvasGroup();
|
|
EnsureOverlayPanelRoot();
|
|
|
|
if (show && !overlayRoot.activeSelf)
|
|
overlayRoot.SetActive(true);
|
|
// Pause entry animation helper may be unavailable in some compile targets.
|
|
// Keep pause functionality independent from that optional visual effect.
|
|
|
|
float startA = overlayCanvasGroup != null ? overlayCanvasGroup.alpha : (show ? 0f : 1f);
|
|
float startPanelA = overlayPanelCanvasGroup != null ? overlayPanelCanvasGroup.alpha : startA;
|
|
float targetA = show ? 1f : 0f;
|
|
Vector3 startScale = overlayPanelRoot != null ? overlayPanelRoot.localScale : panelShownScale;
|
|
if (show && IsScaleNearlyZero(startScale))
|
|
startScale = PanelHiddenScale;
|
|
Vector3 targetScale = show ? panelShownScale : PanelHiddenScale;
|
|
|
|
float duration = Mathf.Max(0.01f, overlayFadeDuration);
|
|
float t = 0f;
|
|
|
|
if (show)
|
|
{
|
|
if (overlayCanvasGroup != null)
|
|
{
|
|
overlayCanvasGroup.interactable = true;
|
|
overlayCanvasGroup.blocksRaycasts = true;
|
|
}
|
|
if (overlayPanelCanvasGroup != null)
|
|
{
|
|
overlayPanelCanvasGroup.interactable = true;
|
|
overlayPanelCanvasGroup.blocksRaycasts = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (overlayCanvasGroup != null)
|
|
{
|
|
overlayCanvasGroup.interactable = false;
|
|
overlayCanvasGroup.blocksRaycasts = false;
|
|
}
|
|
if (overlayPanelCanvasGroup != null)
|
|
{
|
|
overlayPanelCanvasGroup.interactable = false;
|
|
overlayPanelCanvasGroup.blocksRaycasts = false;
|
|
}
|
|
}
|
|
|
|
while (t < duration)
|
|
{
|
|
t += Time.unscaledDeltaTime;
|
|
float k = Mathf.Clamp01(t / duration);
|
|
if (overlayCanvasGroup != null)
|
|
overlayCanvasGroup.alpha = Mathf.Lerp(startA, targetA, k);
|
|
if (overlayPanelCanvasGroup != null)
|
|
overlayPanelCanvasGroup.alpha = Mathf.Lerp(startPanelA, targetA, k);
|
|
if (overlayPanelRoot != null)
|
|
overlayPanelRoot.localScale = Vector3.Lerp(startScale, targetScale, k);
|
|
yield return null;
|
|
}
|
|
|
|
if (overlayCanvasGroup != null)
|
|
overlayCanvasGroup.alpha = targetA;
|
|
if (overlayPanelCanvasGroup != null)
|
|
overlayPanelCanvasGroup.alpha = targetA;
|
|
if (overlayPanelRoot != null)
|
|
overlayPanelRoot.localScale = targetScale;
|
|
|
|
if (!show)
|
|
overlayRoot.SetActive(false);
|
|
|
|
overlayFadeCoroutine = null;
|
|
}
|
|
|
|
private IEnumerator ContinueFromPauseCoroutine()
|
|
{
|
|
// Start hiding panel, but do not wait before countdown starts.
|
|
ShowOverlayAnimated(false);
|
|
|
|
// Replay 3-2-1-Go immediately on continue click.
|
|
var gm = FindAnyObjectByType<GameManager>();
|
|
var ready = gm != null ? gm.readyLetsGo : FindAnyObjectByType<readyLetsGo>();
|
|
float originalWaitingDuration = 0f;
|
|
bool waitingDurationOverridden = false;
|
|
if (ready != null)
|
|
{
|
|
// Remove extra idle delay before countdown visuals in continue flow.
|
|
originalWaitingDuration = ready.waitingDuration;
|
|
ready.waitingDuration = 0f;
|
|
waitingDurationOverridden = true;
|
|
|
|
try { ready.ResetPlayState(); } catch { }
|
|
try { ready.PlaySequence(); } catch { }
|
|
|
|
int imageCount = ready._321goImages != null ? ready._321goImages.Count : 0;
|
|
float sequenceDuration = 0.25f
|
|
+ 0f
|
|
+ Mathf.Max(0f, imageCount * ready.oneImageDuration)
|
|
+ 0.15f;
|
|
if (sequenceDuration > 0f)
|
|
yield return new WaitForSecondsRealtime(sequenceDuration);
|
|
}
|
|
|
|
if (waitingDurationOverridden && ready != null)
|
|
{
|
|
ready.waitingDuration = originalWaitingDuration;
|
|
}
|
|
|
|
// Resume chart + music.
|
|
Pause(false);
|
|
continueCoroutine = null;
|
|
}
|
|
|
|
private IEnumerator ReplayFromPauseCoroutine()
|
|
{
|
|
SetOverlayVisibleImmediate(false);
|
|
IsPaused = false;
|
|
Time.timeScale = 1f;
|
|
try { OnPauseStateChanged?.Invoke(false); } catch { }
|
|
|
|
var bmm = FindAnyObjectByType<BeatmapManager>();
|
|
if (bmm != null && bmm.assignedSongData != null)
|
|
{
|
|
int diff = bmm.assignedDifficulty;
|
|
if (diff < 0) diff = 0;
|
|
BeatmapManager.SetPendingSong(bmm.assignedSongData, diff);
|
|
}
|
|
|
|
string current = SceneManager.GetActiveScene().name;
|
|
yield return LoadSceneAsync(current);
|
|
}
|
|
|
|
private IEnumerator ExitFromPauseCoroutine()
|
|
{
|
|
SetOverlayVisibleImmediate(false);
|
|
IsPaused = false;
|
|
Time.timeScale = 1f;
|
|
try { OnPauseStateChanged?.Invoke(false); } catch { }
|
|
|
|
yield return LoadSceneAsync(ExitSceneName);
|
|
}
|
|
|
|
private IEnumerator LoadSceneAsync(string sceneName)
|
|
{
|
|
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
|
|
while (!asyncLoad.isDone)
|
|
yield return null;
|
|
}
|
|
|
|
private void TryBindButtons(bool force)
|
|
{
|
|
if (buttonsBound && !force) return;
|
|
|
|
if (onlyInGameplayScene && !IsGameplaySceneActive() && !force)
|
|
{
|
|
nextBindRetryTime = Time.unscaledTime + 1f;
|
|
return;
|
|
}
|
|
|
|
if (pauseTriggerButton == null)
|
|
{
|
|
var pauseGO = GameObject.Find(pauseButtonScenePath);
|
|
if (pauseGO == null)
|
|
pauseGO = FindByNameInScene("Pause");
|
|
if (pauseGO != null)
|
|
pauseTriggerButton = pauseGO.GetComponent<Button>();
|
|
}
|
|
|
|
if (overlayRoot != null)
|
|
{
|
|
if (continueButton == null) continueButton = FindButtonInOverlay("Continue");
|
|
if (replayButton == null) replayButton = FindButtonInOverlay("Replay");
|
|
if (exitButton == null) exitButton = FindButtonInOverlay("Exit");
|
|
if (settingsButton == null) settingsButton = FindButtonInOverlay("settings");
|
|
}
|
|
|
|
if (pauseTriggerButton != null)
|
|
{
|
|
pauseTriggerButton.onClick.RemoveListener(OnPauseButtonClicked);
|
|
pauseTriggerButton.onClick.AddListener(OnPauseButtonClicked);
|
|
}
|
|
|
|
if (continueButton != null)
|
|
{
|
|
continueButton.onClick.RemoveListener(ContinueFromPause);
|
|
continueButton.onClick.AddListener(ContinueFromPause);
|
|
}
|
|
|
|
if (replayButton != null)
|
|
{
|
|
replayButton.onClick.RemoveListener(ReplayFromPause);
|
|
replayButton.onClick.AddListener(ReplayFromPause);
|
|
}
|
|
|
|
if (exitButton != null)
|
|
{
|
|
exitButton.onClick.RemoveListener(ExitFromPause);
|
|
exitButton.onClick.AddListener(ExitFromPause);
|
|
}
|
|
|
|
if (settingsButton != null)
|
|
{
|
|
settingsButton.onClick.RemoveListener(OnSettingsButtonClicked);
|
|
settingsButton.onClick.AddListener(OnSettingsButtonClicked);
|
|
}
|
|
|
|
buttonsBound = pauseTriggerButton != null && continueButton != null && replayButton != null && exitButton != null;
|
|
nextBindRetryTime = Time.unscaledTime + (buttonsBound ? 9999f : 1f);
|
|
}
|
|
|
|
private void OnPauseButtonClicked()
|
|
{
|
|
if (!CanPauseFromInput()) return;
|
|
if (!IsPaused) Pause(true);
|
|
}
|
|
|
|
private void OnSettingsButtonClicked()
|
|
{
|
|
if (!IsPaused) return;
|
|
if (TryOpenExistingSettingsPanel()) return;
|
|
|
|
// NEW: Try to instantiate from prefab if provided
|
|
if (settingsPrefab != null)
|
|
{
|
|
GameObject newSettings = Instantiate(settingsPrefab);
|
|
newSettings.name = "ui_Panel_Setting";
|
|
|
|
// Ensure it has required animation component if needed by PauseManager logic
|
|
if (newSettings.GetComponent<UI_SettingsPanelEnterAnim>() == null)
|
|
{
|
|
newSettings.AddComponent<UI_SettingsPanelEnterAnim>();
|
|
}
|
|
|
|
newSettings.SetActive(true);
|
|
newSettings.transform.SetAsLastSibling();
|
|
return;
|
|
}
|
|
|
|
var topController = FindAnyObjectByType<btmandtopController>();
|
|
if (topController != null)
|
|
{
|
|
topController.SendMessage("OnSettingsNavClicked", SendMessageOptions.DontRequireReceiver);
|
|
return;
|
|
}
|
|
|
|
Debug.LogWarning("[PauseManager] Settings button clicked, but no settings panel source was found.");
|
|
}
|
|
|
|
private bool TryOpenExistingSettingsPanel()
|
|
{
|
|
var settingsPanels = Resources.FindObjectsOfTypeAll<ui_Panel_Setting>();
|
|
for (int i = 0; i < settingsPanels.Length; i++)
|
|
{
|
|
ui_Panel_Setting panel = settingsPanels[i];
|
|
if (panel == null) continue;
|
|
GameObject go = panel.gameObject;
|
|
if (go == null || !go.scene.IsValid()) continue;
|
|
|
|
go.SetActive(true);
|
|
go.transform.SetAsLastSibling();
|
|
|
|
CanvasGroup cg = go.GetComponent<CanvasGroup>();
|
|
if (cg != null)
|
|
{
|
|
cg.interactable = true;
|
|
cg.blocksRaycasts = true;
|
|
}
|
|
|
|
UI_SettingsPanelEnterAnim enterAnim = go.GetComponent<UI_SettingsPanelEnterAnim>();
|
|
if (enterAnim != null) enterAnim.Play();
|
|
return true;
|
|
}
|
|
|
|
GameObject byName = FindByNameInScene("ui_Panel_Setting");
|
|
if (byName != null)
|
|
{
|
|
byName.SetActive(true);
|
|
byName.transform.SetAsLastSibling();
|
|
UI_SettingsPanelEnterAnim enterAnim = byName.GetComponent<UI_SettingsPanelEnterAnim>();
|
|
if (enterAnim != null) enterAnim.Play();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private Button FindButtonInOverlay(string buttonName)
|
|
{
|
|
if (overlayRoot == null || string.IsNullOrEmpty(buttonName)) return null;
|
|
|
|
var buttons = overlayRoot.GetComponentsInChildren<Button>(true);
|
|
for (int i = 0; i < buttons.Length; i++)
|
|
{
|
|
var b = buttons[i];
|
|
if (b == null) continue;
|
|
if (string.Equals(b.gameObject.name, buttonName, StringComparison.OrdinalIgnoreCase))
|
|
return b;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private GameObject FindByNameInScene(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name)) return null;
|
|
|
|
var all = Resources.FindObjectsOfTypeAll<Transform>();
|
|
for (int i = 0; i < all.Length; i++)
|
|
{
|
|
var t = all[i];
|
|
if (t == null) continue;
|
|
if (!string.Equals(t.name, name, StringComparison.OrdinalIgnoreCase)) continue;
|
|
if (t.gameObject.scene.IsValid())
|
|
return t.gameObject;
|
|
}
|
|
return null;
|
|
}
|
|
}
|