成就系统 巨大修改 新的ui 黑白效果 等一堆
This commit is contained in:
@@ -3,24 +3,41 @@ using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.Collections;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngineInternal;
|
||||
|
||||
public class GameManager : MonoBehaviour
|
||||
{
|
||||
public BeatmapManager beatmapManager; // ������غ�������
|
||||
public NoteSpawner noteSpawner; // ����������
|
||||
public AudioSource musicSource; // ���ֲ�����
|
||||
public BeatmapManager beatmapManager; // Beatmap manager
|
||||
public NoteSpawner noteSpawner; // Note spawner
|
||||
public AudioSource musicSource; // Music audio source
|
||||
public readyLetsGo readyLetsGo;
|
||||
|
||||
[Header("paths")]
|
||||
// �����������ٴӾ�̬�����ж�ȡ
|
||||
// Paths for external test-mode resources
|
||||
public string JSONPath;
|
||||
public string audioPath;
|
||||
public string bgPicPath;
|
||||
|
||||
// Optional: sprite renderer to show background image
|
||||
[Header("canvas")]
|
||||
public GameObject startCanvas;
|
||||
public CanvasGroup cg_startCanvas;
|
||||
public float canvasFadeTime;
|
||||
private Coroutine fade_canvasGroup;
|
||||
public CanvasGroup settleCG;
|
||||
|
||||
[Header("buttons")]
|
||||
public Button startGame;
|
||||
public Button backtoSelectingPage;
|
||||
|
||||
[Header("sprite renderer")]
|
||||
public SpriteRenderer backgroundRenderer;
|
||||
|
||||
// NEW: UI Text to display banflag content from pressStart
|
||||
[Header("Debug / Test")]
|
||||
public Text banflagTextUI;
|
||||
// new: display current song name on UI when assigning background
|
||||
public Text songnameText;
|
||||
|
||||
[Header("UI Overlays")]
|
||||
public UnityEngine.UI.Image blackMaskImage; // assign in inspector: full-screen black overlay
|
||||
@@ -29,9 +46,25 @@ public class GameManager : MonoBehaviour
|
||||
[Tooltip("Delay in seconds after unpausing (press Space) before starting audio and spawning. Configure in inspector.")]
|
||||
public float playbackStartDelay = 3f;
|
||||
|
||||
[Tooltip("Delay in seconds before starting the black mask fade out. Configure in inspector.")]
|
||||
public float blackMaskFadeStartDelay = 1.5f;
|
||||
|
||||
private bool pauseSubscribed = false;
|
||||
private bool musicWasPlayingBeforePause = false;
|
||||
|
||||
// tracks whether actual music playback (the real game playback) has started
|
||||
public bool PlaybackStarted { get; private set; } = false;
|
||||
|
||||
// Flag set by UI button to request start
|
||||
private bool startRequested = false;
|
||||
// Flag to control whether Escape can trigger return (disabled once startRequested is true)
|
||||
private bool allowEscapeReturn = true;
|
||||
|
||||
// --- Statistics Tracking ---
|
||||
private float sessionStartTime;
|
||||
private SongData currentSong;
|
||||
private bool timeRecorded = false;
|
||||
|
||||
private void SubscribeToPauseManager()
|
||||
{
|
||||
if (pauseSubscribed) return;
|
||||
@@ -79,10 +112,15 @@ public class GameManager : MonoBehaviour
|
||||
{
|
||||
musicSource.Pause();
|
||||
}
|
||||
|
||||
// Also pause Time.timeScale so playback delay coroutines are also paused
|
||||
Time.timeScale = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do not change mute state here. External startup flow will unmute when appropriate.
|
||||
// Resume Time.timeScale first
|
||||
Time.timeScale = 1f;
|
||||
|
||||
// resume if it was playing before pause
|
||||
if (musicWasPlayingBeforePause)
|
||||
{
|
||||
@@ -130,94 +168,194 @@ public class GameManager : MonoBehaviour
|
||||
blackMaskImage.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private Coroutine playbackDelayCoroutine;
|
||||
|
||||
// Public helper to reliably unmute and start music playback, respecting delay.
|
||||
public void PlayMusicWithDelay(float delaySeconds)
|
||||
{
|
||||
// reset flag until actual play occurs
|
||||
PlaybackStarted = false;
|
||||
|
||||
// Stop any previous delay coroutine
|
||||
if (playbackDelayCoroutine != null)
|
||||
{
|
||||
StopCoroutine(playbackDelayCoroutine);
|
||||
playbackDelayCoroutine = null;
|
||||
}
|
||||
|
||||
if (musicSource == null) return;
|
||||
|
||||
// ✅ FIX: Force stop any existing playback before unmute
|
||||
try
|
||||
{
|
||||
// Ensure unmuted
|
||||
musicSource.Stop();
|
||||
musicSource.time = 0f;
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
// ✅ FIX: Ensure unmuted BEFORE attempting to play
|
||||
musicSource.mute = false;
|
||||
Debug.Log("GameManager.PlayMusicWithDelay: musicSource unmuted");
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
// If a delay is specified, stop current and schedule playback
|
||||
// Reset audio time to 0 before playing to ensure playback starts from the beginning
|
||||
musicSource.time = 0f;
|
||||
|
||||
if (delaySeconds > 0f)
|
||||
{
|
||||
// Stop any currently playing to avoid overlapping schedules
|
||||
try { musicSource.Stop(); } catch { }
|
||||
musicSource.PlayDelayed(delaySeconds);
|
||||
Debug.Log($"GameManager.PlayMusicWithDelay: PlayDelayed({delaySeconds}) called");
|
||||
// Use coroutine instead of PlayDelayed so pause state can interrupt it
|
||||
playbackDelayCoroutine = StartCoroutine(DelayAndPlayMusic(delaySeconds));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If already playing but paused, try UnPause; otherwise Play
|
||||
if (musicSource.isPlaying)
|
||||
{
|
||||
// already playing - nothing to do
|
||||
Debug.Log("GameManager.PlayMusicWithDelay: musicSource already playing");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try UnPause first (in case it was paused), otherwise Play
|
||||
try { musicSource.UnPause(); Debug.Log("GameManager.PlayMusicWithDelay: UnPause called"); }
|
||||
catch { musicSource.Play(); Debug.Log("GameManager.PlayMusicWithDelay: Play called"); }
|
||||
}
|
||||
// Immediate playback (no delay)
|
||||
musicSource.time = 0f;
|
||||
musicSource.Play();
|
||||
PlaybackStarted = true;
|
||||
Debug.Log("GameManager.PlayMusicWithDelay: Music playback started immediately (no delay)");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"PlayMusicWithDelay failed: {ex}");
|
||||
try { musicSource.Play(); } catch { }
|
||||
try
|
||||
{
|
||||
musicSource.time = 0f;
|
||||
musicSource.Play();
|
||||
PlaybackStarted = true;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator DelayAndPlayMusic(float delaySeconds)
|
||||
{
|
||||
Debug.Log($"GameManager.DelayAndPlayMusic: waiting {delaySeconds} seconds before playing (respects pause state)");
|
||||
|
||||
// Wait using unscaled real time so this delay is not blocked when Time.timeScale==0
|
||||
float elapsed = 0f;
|
||||
while (elapsed < delaySeconds)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// After delay, play the music
|
||||
if (musicSource != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// ✅ FIX: Double-check unmute before actual playback
|
||||
musicSource.mute = false;
|
||||
musicSource.time = 0f;
|
||||
musicSource.Play();
|
||||
PlaybackStarted = true;
|
||||
Debug.Log($"GameManager.DelayAndPlayMusic: music playback started after {delaySeconds}s delay, mute={musicSource.mute}");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"DelayAndPlayMusic failed to play: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
playbackDelayCoroutine = null;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
Debug.Log("GameManager Start() ������");
|
||||
Debug.Log("GameManager Start() initialized");
|
||||
|
||||
// ���ؼ�����Ƿ�Ϊ��
|
||||
if (beatmapManager == null) Debug.LogError("beatmapManager δ��ֵ��");
|
||||
if (noteSpawner == null) Debug.LogError("noteSpawner δ��ֵ��");
|
||||
if (musicSource == null) Debug.LogError("musicSource δ��ֵ��");
|
||||
// --- Initialize Statistics ---
|
||||
currentSong = BeatmapManager.pendingSongData ?? SongDataHolder.SelectedSongData;
|
||||
if (currentSong != null)
|
||||
{
|
||||
currentSong.game_enterTimes++;
|
||||
sessionStartTime = Time.realtimeSinceStartup;
|
||||
Debug.Log($"[GameManager] {currentSong.songName} launch count incremented to {currentSong.game_enterTimes}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[GameManager] No SongData found for statistics tracking");
|
||||
}
|
||||
|
||||
// Ensure start canvas and its CanvasGroup are active and visible at scene start
|
||||
if (startCanvas != null && !startCanvas.activeSelf) startCanvas.SetActive(true);
|
||||
if (cg_startCanvas != null)
|
||||
{
|
||||
cg_startCanvas.alpha = 1f;
|
||||
cg_startCanvas.interactable = true;
|
||||
cg_startCanvas.blocksRaycasts = true;
|
||||
}
|
||||
|
||||
// Try to apply assigned SongData (if any) to background and UI after scene load
|
||||
StartCoroutine(WaitAndApplyAssignedSong(2f));
|
||||
|
||||
if (beatmapManager == null) Debug.LogError("beatmapManager is null");
|
||||
if (noteSpawner == null) Debug.LogError("noteSpawner is null");
|
||||
if (musicSource == null) Debug.LogError("musicSource is null");
|
||||
|
||||
// Subscribe to NoteSpawner.AllNotesSpawned to trigger action when spawning completes
|
||||
if (noteSpawner != null)
|
||||
{
|
||||
noteSpawner.AllNotesSpawned += OnAllNotesSpawned;
|
||||
}
|
||||
|
||||
// Wire up UI buttons if assigned
|
||||
if (startGame != null)
|
||||
{
|
||||
// ensure we don't register duplicate listeners
|
||||
try { startGame.onClick.RemoveListener(RequestStart); } catch { }
|
||||
startGame.onClick.AddListener(RequestStart);
|
||||
}
|
||||
if (backtoSelectingPage != null)
|
||||
{
|
||||
try { backtoSelectingPage.onClick.RemoveListener(BackToSelectingPage); } catch { }
|
||||
backtoSelectingPage.onClick.AddListener(BackToSelectingPage);
|
||||
}
|
||||
|
||||
// preload audio fully and start muted playback to warm up audio decoding
|
||||
if (musicSource != null && musicSource.clip != null)
|
||||
{
|
||||
musicSource.mute = true;
|
||||
musicSource.loop = false;
|
||||
musicSource.playOnAwake = false;
|
||||
try
|
||||
{
|
||||
// Warm up decoding by playing briefly muted so the audio system decodes the clip.
|
||||
musicSource.mute = true;
|
||||
musicSource.Play();
|
||||
Debug.Log("GameManager: warmed up audio playback (muted)");
|
||||
// ✅ FIX: Stop immediately after warmup to avoid interference with later playback
|
||||
// Wait a frame to ensure audio system processes the Play command
|
||||
StartCoroutine(StopWarmupAfterFrame());
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogWarning("GameManager: failed to warm up audio: " + ex.Message);
|
||||
}
|
||||
|
||||
settleCG.alpha = 0;
|
||||
}
|
||||
|
||||
// ��� pressStart �Ѿ���ȡ�� banflag ���ݣ��������ʾ�� UI����������ˣ�
|
||||
// display banflag content if available
|
||||
if (banflagTextUI != null && !string.IsNullOrEmpty(pressStart.banflagContent))
|
||||
{
|
||||
banflagTextUI.text = pressStart.banflagContent;
|
||||
Debug.Log("Displayed banflag content from pressStart into banflagTextUI.");
|
||||
}
|
||||
|
||||
// �� test mode ��ʹ���ⲿ·��Ԥ������Դ����ͣϵͳ���ȴ����ո����
|
||||
// choose startup path
|
||||
if (GameConfig.testMode)
|
||||
{
|
||||
// �ӳٸ�ֵ���� Start() �л�ȡ��̬·������ʱ pressStart.cs �Ѿ����в�����������
|
||||
JSONPath = pressStart.testmode_beatmapJSON_path;
|
||||
audioPath = pressStart.testmode_audioFile_path;
|
||||
bgPicPath = pressStart.testmode_bgPicFile_path;
|
||||
|
||||
Debug.Log("Test mode active: using external paths to preload assets");
|
||||
|
||||
// also update banflag UI if content exists
|
||||
if (banflagTextUI != null && !string.IsNullOrEmpty(pressStart.banflagContent))
|
||||
{
|
||||
banflagTextUI.text = pressStart.banflagContent;
|
||||
@@ -241,13 +379,167 @@ public class GameManager : MonoBehaviour
|
||||
blackMaskImage.color = cc;
|
||||
blackMaskImage.gameObject.SetActive(true);
|
||||
blackMaskImage.raycastTarget = true;
|
||||
// start automatic fade from alpha=1 to 0 over 1 second after Start
|
||||
StartCoroutine(FadeOutBlackMask(1f));
|
||||
// start automatic fade from alpha=1 to 0 over 1 second after Start, with delay
|
||||
StartCoroutine(DelayedFadeOutBlackMask(blackMaskFadeStartDelay, 1f));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper coroutine to stop audio warmup after one frame and unmute the source
|
||||
/// </summary>
|
||||
private IEnumerator StopWarmupAfterFrame()
|
||||
{
|
||||
yield return null; // wait one frame for audio system to process Play command
|
||||
|
||||
if (musicSource != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
musicSource.Stop();
|
||||
musicSource.mute = false; // ✅ Unmute immediately after warmup
|
||||
musicSource.time = 0f;
|
||||
Debug.Log("GameManager: warmup completed, audio stopped and unmuted");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogWarning("GameManager: failed to stop warmup: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// Detect Space or yellow note key to request start (only before gameplay starts)
|
||||
if (!startRequested)
|
||||
{
|
||||
KeyCode yellowKey = KeyBindingManager.GetKeyForColor("yellow");
|
||||
// Check Space key
|
||||
if (Input.GetKeyDown(KeyCode.Space))
|
||||
{
|
||||
RequestStart();
|
||||
}
|
||||
// Check yellow track key from key binding manager
|
||||
else if (yellowKey != KeyCode.None && Input.GetKeyDown(yellowKey))
|
||||
{
|
||||
if (yellowKey == KeyCode.Space) return;
|
||||
else RequestStart();
|
||||
}
|
||||
|
||||
// Check Escape key to trigger return to selecting page (only before gameplay starts and allowed)
|
||||
if (allowEscapeReturn && Input.GetKeyDown(KeyCode.Escape))
|
||||
{
|
||||
BackToSelectingPage();
|
||||
}
|
||||
}
|
||||
// Once startRequested is true, Escape is disabled and gameplay begins countdown
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
RecordTotalPlayTime();
|
||||
// Unsubscribe from NoteSpawner.AllNotesSpawned to avoid memory leaks
|
||||
if (noteSpawner != null)
|
||||
{
|
||||
noteSpawner.AllNotesSpawned -= OnAllNotesSpawned;
|
||||
}
|
||||
|
||||
// remove UI listeners
|
||||
if (startGame != null)
|
||||
{
|
||||
startGame.onClick.RemoveListener(RequestStart);
|
||||
}
|
||||
if (backtoSelectingPage != null)
|
||||
{
|
||||
backtoSelectingPage.onClick.RemoveListener(BackToSelectingPage);
|
||||
}
|
||||
|
||||
// stop canvas fade coroutine if running
|
||||
if (fade_canvasGroup != null)
|
||||
{
|
||||
StopCoroutine(fade_canvasGroup);
|
||||
fade_canvasGroup = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Start the fade coroutine for the start canvas group (safe to call multiple times)
|
||||
private void StartFadeStartCanvas()
|
||||
{
|
||||
if (cg_startCanvas == null || startCanvas == null) return;
|
||||
if (fade_canvasGroup != null) StopCoroutine(fade_canvasGroup);
|
||||
fade_canvasGroup = StartCoroutine(FadeStartCanvasCoroutine(canvasFadeTime));
|
||||
}
|
||||
|
||||
private IEnumerator FadeStartCanvasCoroutine(float duration)
|
||||
{
|
||||
if (cg_startCanvas == null || startCanvas == null) yield break;
|
||||
|
||||
float elapsed = 0f;
|
||||
float startA = cg_startCanvas.alpha;
|
||||
while (elapsed < duration)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float frac = Mathf.Clamp01(elapsed / Mathf.Max(0.0001f, duration));
|
||||
cg_startCanvas.alpha = Mathf.Lerp(startA, 0f, frac);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
cg_startCanvas.alpha = 0f;
|
||||
cg_startCanvas.interactable = false;
|
||||
cg_startCanvas.blocksRaycasts = false;
|
||||
startCanvas.SetActive(false);
|
||||
fade_canvasGroup = null;
|
||||
}
|
||||
|
||||
private void OnAllNotesSpawned()
|
||||
{
|
||||
// All notes have been spawned - trigger settlement decision logic
|
||||
Debug.Log("[GameManager] All notes have been spawned. Initiating settlement countdown.");
|
||||
|
||||
if (noteSpawner != null)
|
||||
{
|
||||
noteSpawner.StartSettlementRoutine();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[GameManager] OnAllNotesSpawned: noteSpawner is null, cannot start settlement routine");
|
||||
}
|
||||
}
|
||||
|
||||
// Called by UI button to request the start countdown
|
||||
public void RequestStart()
|
||||
{
|
||||
// Guard: prevent duplicate RequestStart execution
|
||||
if (startRequested)
|
||||
{
|
||||
Debug.LogWarning("[GameManager] RequestStart already called, ignoring duplicate request");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("[GameManager] RequestStart executed");
|
||||
startRequested = true;
|
||||
allowEscapeReturn = false; // Disable Escape immediately when start is requested
|
||||
|
||||
// Play ready sequence if available (null-check to avoid NullReferenceException)
|
||||
if (readyLetsGo != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
readyLetsGo.PlaySequence();
|
||||
Debug.Log("[GameManager] readyLetsGo.PlaySequence() triggered");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"[GameManager] Error: {ex}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[GameManager] readyLetsGo is null");
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator HandleTestModeStartup()
|
||||
{
|
||||
// Preload audio from audioPath (absolute)
|
||||
@@ -320,15 +612,16 @@ public class GameManager : MonoBehaviour
|
||||
musicSource.clip = clip;
|
||||
musicSource.loop = false;
|
||||
musicSource.playOnAwake = false;
|
||||
// Warm up decoding by playing briefly muted; avoid try/catch around yield (not allowed).
|
||||
// Warm up decoding by playing briefly muted so the audio system decodes the clip.
|
||||
musicSource.mute = true;
|
||||
musicSource.Play();
|
||||
// allow a few frames for audio system to start
|
||||
yield return null;
|
||||
musicSource.Stop();
|
||||
|
||||
Debug.Log("TestMode audio loaded into AudioSource");
|
||||
|
||||
// ✅ FIX: Stop immediately after warmup to avoid interference with later playback
|
||||
StartCoroutine(StopWarmupAfterFrame());
|
||||
|
||||
// After loading audio, enable overlay and pause via PauseManager
|
||||
var pm = PauseManager.Instance ?? FindObjectOfType<PauseManager>();
|
||||
if (pm != null)
|
||||
@@ -365,7 +658,7 @@ public class GameManager : MonoBehaviour
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"��ȡ����ͼƬ�ļ�����: {ex}");
|
||||
Debug.LogError($"Failed to read background image file: {ex}");
|
||||
}
|
||||
|
||||
if (imgBytes != null)
|
||||
@@ -376,29 +669,34 @@ public class GameManager : MonoBehaviour
|
||||
Sprite s = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
|
||||
backgroundRenderer.sprite = s;
|
||||
|
||||
// ���ĵ㣺���� SpriteRenderer ����Ϊ 255 (1.0f)��
|
||||
// ensure SpriteRenderer alpha is fully opaque
|
||||
Color color = backgroundRenderer.color;
|
||||
color.a = 1.0f;
|
||||
backgroundRenderer.color = color;
|
||||
|
||||
// ���ֱ�������������Ӧ�ɳ������־�������¼ assignment
|
||||
Debug.Log("Background sprite assigned (ensure your SpriteRenderer size fits scene)");
|
||||
|
||||
// Set song name text if available
|
||||
if (songnameText != null)
|
||||
{
|
||||
songnameText.text = Path.GetFileNameWithoutExtension(bgPicPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"�����ֽڴ��� Texture2D: {bgPicPath}");
|
||||
Debug.LogError($"Failed to create Texture2D from bytes: {bgPicPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("TestMode bgPicPath δ���á�");
|
||||
Debug.LogWarning("TestMode bgPicPath not provided");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("backgroundRenderer δ���ã������������ء�");
|
||||
Debug.LogWarning("backgroundRenderer not assigned; skipping background preload");
|
||||
}
|
||||
|
||||
// Read JSON from JSONPath and parse via BeatmapManager.ParseJsonOnly
|
||||
@@ -417,14 +715,14 @@ public class GameManager : MonoBehaviour
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"��ȡ TestMode JSON �ļ�����: {ex}");
|
||||
Debug.LogError($"Failed to read TestMode JSON file: {ex}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
bool parsed = beatmapManager.ParseJsonOnly(jsonContent);
|
||||
if (!parsed)
|
||||
{
|
||||
Debug.LogError("ParseJsonOnly ʧ�ܣ�ֹͣ��������ģʽ��");
|
||||
Debug.LogError("ParseJsonOnly failed; aborting test mode startup");
|
||||
yield break;
|
||||
}
|
||||
|
||||
@@ -433,7 +731,7 @@ public class GameManager : MonoBehaviour
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("TestMode JSONPath δ���á�");
|
||||
Debug.LogError("TestMode JSONPath not provided");
|
||||
yield break;
|
||||
}
|
||||
|
||||
@@ -443,16 +741,25 @@ public class GameManager : MonoBehaviour
|
||||
PauseManager.Instance?.Pause(true);
|
||||
UpdateStatusOnConsole("Paused: press Space to start playback");
|
||||
|
||||
// Wait for space key down (use unscaled input loop)
|
||||
while (!Input.GetKeyDown(KeyCode.Space))
|
||||
// Determine yellow track key from key binding manager
|
||||
KeyCode yellowKey = KeyBindingManager.GetKeyForColor("yellow");
|
||||
|
||||
// Wait for start request: Space, yellow key, or UI button
|
||||
while (!startRequested && !Input.GetKeyDown(KeyCode.Space) && (yellowKey == KeyCode.None || !Input.GetKeyDown(yellowKey)))
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Clear request flag
|
||||
startRequested = false;
|
||||
|
||||
// Kick off UI canvas fade-out when start requested
|
||||
StartFadeStartCanvas();
|
||||
|
||||
// Resume using PauseManager
|
||||
PauseManager.Instance?.Pause(false);
|
||||
// small buffer to avoid hitching immediately after unpause: wait configured realtime delay
|
||||
yield return new WaitForSecondsRealtime(Mathf.Max(0f, playbackStartDelay));
|
||||
// Use scaled wait so pause (Escape) will pause this delay
|
||||
yield return new WaitForSeconds(Mathf.Max(0f, playbackStartDelay));
|
||||
|
||||
// Start spawning using the parsed beatmap
|
||||
if (beatmapManager.beatmap != null)
|
||||
@@ -461,7 +768,7 @@ public class GameManager : MonoBehaviour
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("��������ʧ�ܣ�beatmap δ������Ϊ��");
|
||||
Debug.LogError("Beatmap missing after parsing");
|
||||
yield break;
|
||||
}
|
||||
|
||||
@@ -484,16 +791,25 @@ public class GameManager : MonoBehaviour
|
||||
PauseManager.Instance?.Pause(true);
|
||||
UpdateStatusOnConsole("Paused: press Space to start playback");
|
||||
|
||||
// Wait for space
|
||||
while (!Input.GetKeyDown(KeyCode.Space))
|
||||
// Determine yellow track key from key binding manager
|
||||
KeyCode yellowKey = KeyBindingManager.GetKeyForColor("yellow");
|
||||
|
||||
// Wait for start request: Space, yellow key, or UI button
|
||||
while (!startRequested && !Input.GetKeyDown(KeyCode.Space) && (yellowKey == KeyCode.None || !Input.GetKeyDown(yellowKey)))
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Clear request flag
|
||||
startRequested = false;
|
||||
|
||||
// Kick off UI canvas fade-out when start requested
|
||||
StartFadeStartCanvas();
|
||||
|
||||
// Resume
|
||||
PauseManager.Instance?.Pause(false);
|
||||
// small buffer to avoid hitching immediately after unpause: wait configured realtime delay
|
||||
yield return new WaitForSecondsRealtime(Mathf.Max(0f, playbackStartDelay));
|
||||
// Use scaled wait so pause (Escape) will pause this delay
|
||||
yield return new WaitForSeconds(Mathf.Max(0f, playbackStartDelay));
|
||||
|
||||
// Start spawning using the existing parsed beatmap
|
||||
if (beatmapManager.beatmap != null)
|
||||
@@ -542,12 +858,12 @@ public class GameManager : MonoBehaviour
|
||||
yield break;
|
||||
}
|
||||
|
||||
// ԭ�����̣��������浫����������
|
||||
// Original default demo load commented out; manual load only when needed
|
||||
string beatmapFilePath = Path.Combine(Application.streamingAssetsPath, "Emilia_demo.json");
|
||||
|
||||
if (!File.Exists(beatmapFilePath))
|
||||
{
|
||||
Debug.LogError($"�����������: {beatmapFilePath}");
|
||||
Debug.LogError($"Beatmap file missing: {beatmapFilePath}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
@@ -555,55 +871,62 @@ public class GameManager : MonoBehaviour
|
||||
bool parsed = beatmapManager.ParseJsonOnly(json);
|
||||
if (!parsed)
|
||||
{
|
||||
Debug.LogError("ParseJsonOnly ʧ��");
|
||||
Debug.LogError("ParseJsonOnly failed");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// ��ͣϵͳ
|
||||
// Pause system
|
||||
SubscribeToPauseManager();
|
||||
PauseManager.Instance?.Pause(true);
|
||||
UpdateStatusOnConsole("Paused: press Space to start playback");
|
||||
|
||||
// �ȴ��ո�
|
||||
while (!Input.GetKeyDown(KeyCode.Space))
|
||||
// Determine yellow track key from key binding manager
|
||||
KeyCode yellowKey2 = KeyBindingManager.GetKeyForColor("yellow");
|
||||
|
||||
// Wait for start request: Space, yellow key, or UI button
|
||||
while (!startRequested && !Input.GetKeyDown(KeyCode.Space) && (yellowKey2 == KeyCode.None || !Input.GetKeyDown(yellowKey2)))
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// �ָ�
|
||||
PauseManager.Instance?.Pause(false);
|
||||
// small buffer to avoid hitching immediately after unpause: wait configured realtime delay
|
||||
yield return new WaitForSecondsRealtime(Mathf.Max(0f, playbackStartDelay));
|
||||
// Clear request flag
|
||||
startRequested = false;
|
||||
|
||||
// ��������
|
||||
// Kick off UI canvas fade-out when start requested
|
||||
StartFadeStartCanvas();
|
||||
|
||||
// Resume
|
||||
PauseManager.Instance?.Pause(false);
|
||||
// Use scaled wait so pause (Escape) will pause this delay
|
||||
yield return new WaitForSeconds(Mathf.Max(0f, playbackStartDelay));
|
||||
|
||||
// Start spawning
|
||||
if (beatmapManager.beatmap != null)
|
||||
{
|
||||
noteSpawner.LoadBeatmap(beatmapManager.beatmap);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("beatmap ���");
|
||||
Debug.LogError("beatmap missing");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// ���ű������֣��ӳٲ����� beatmapManager.globalDelaySeconds ������
|
||||
// Load and play audio
|
||||
if (!string.IsNullOrEmpty(beatmapManager.parsedMusicFile))
|
||||
{
|
||||
AudioClip musicClip = Resources.Load<AudioClip>(beatmapManager.parsedMusicFile);
|
||||
if (musicClip == null)
|
||||
{
|
||||
Debug.LogError($"�����ļ�����ʧ��: {beatmapManager.parsedMusicFile}");
|
||||
Debug.LogError($"Failed to load parsed music file: {beatmapManager.parsedMusicFile}");
|
||||
}
|
||||
else
|
||||
{
|
||||
musicSource.clip = musicClip;
|
||||
// ensure we don't accidentally autoplay when assigning clips
|
||||
musicSource.playOnAwake = false;
|
||||
musicSource.Stop();
|
||||
float delay = beatmapManager.globalDelaySeconds;
|
||||
PlayMusicWithDelay(delay);
|
||||
|
||||
// After assigning clip, enable overlay and pause
|
||||
var pm3 = PauseManager.Instance ?? FindObjectOfType<PauseManager>();
|
||||
if (pm3 != null)
|
||||
{
|
||||
@@ -614,7 +937,7 @@ public class GameManager : MonoBehaviour
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("������ musicFile Ϊ�գ�");
|
||||
Debug.LogError("parsed musicFile empty");
|
||||
}
|
||||
|
||||
UpdateStatusOnConsole("Playback started");
|
||||
@@ -624,4 +947,129 @@ public class GameManager : MonoBehaviour
|
||||
{
|
||||
Debug.Log(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delay then fade out the black mask image. Waits for delaySeconds, then fades out over fadeDuration seconds (unscaled).
|
||||
/// </summary>
|
||||
private IEnumerator DelayedFadeOutBlackMask(float delaySeconds, float fadeDuration)
|
||||
{
|
||||
// Wait for the specified delay
|
||||
float elapsed = 0f;
|
||||
while (elapsed < delaySeconds)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// After delay, start the fade out
|
||||
yield return StartCoroutine(FadeOutBlackMask(fadeDuration));
|
||||
}
|
||||
|
||||
// Called when the back button is pressed; fades to black then loads selection scene
|
||||
private void BackToSelectingPage()
|
||||
{
|
||||
RecordTotalPlayTime();
|
||||
// restore time scale to normal so UI animations driven by unscaled or scaled time behave correctly
|
||||
try { Time.timeScale = 1f; } catch { }
|
||||
|
||||
// start coroutine to fade mask then load
|
||||
if (blackMaskImage == null)
|
||||
{
|
||||
SceneManager.LoadScene("selectYourSongFirst");
|
||||
return;
|
||||
}
|
||||
|
||||
StartCoroutine(FadeToBlackAndLoad("selectYourSongFirst", 0.25f));
|
||||
}
|
||||
|
||||
private IEnumerator FadeToBlackAndLoad(string sceneName, float duration)
|
||||
{
|
||||
RecordTotalPlayTime();
|
||||
if (blackMaskImage == null)
|
||||
{
|
||||
SceneManager.LoadScene(sceneName);
|
||||
yield break;
|
||||
}
|
||||
|
||||
// ensure image active and start from current alpha
|
||||
if (!blackMaskImage.gameObject.activeSelf) blackMaskImage.gameObject.SetActive(true);
|
||||
float startA = blackMaskImage.color.a;
|
||||
float elapsed = 0f;
|
||||
|
||||
// enable raycast target while fading to block input
|
||||
try { blackMaskImage.raycastTarget = true; } catch {}
|
||||
|
||||
while (elapsed < duration)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float frac = Mathf.Clamp01(elapsed / Mathf.Max(0.0001f, duration));
|
||||
Color c = blackMaskImage.color;
|
||||
c.a = Mathf.Lerp(startA, 1f, frac);
|
||||
blackMaskImage.color = c;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// ensure fully opaque
|
||||
Color fc = blackMaskImage.color;
|
||||
fc.a = 1f;
|
||||
blackMaskImage.color = fc;
|
||||
|
||||
// load scene
|
||||
SceneManager.LoadScene(sceneName);
|
||||
}
|
||||
|
||||
private IEnumerator WaitAndApplyAssignedSong(float timeoutSeconds)
|
||||
{
|
||||
float waited = 0f;
|
||||
while (waited < timeoutSeconds)
|
||||
{
|
||||
if (beatmapManager != null && beatmapManager.assignedSongData != null)
|
||||
{
|
||||
ApplyAssignedSongToUI(beatmapManager.assignedSongData);
|
||||
yield break;
|
||||
}
|
||||
waited += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// also consider pendingSongData static as fallback
|
||||
if (BeatmapManager.pendingSongData != null)
|
||||
{
|
||||
ApplyAssignedSongToUI(BeatmapManager.pendingSongData);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyAssignedSongToUI(SongData sd)
|
||||
{
|
||||
if (sd == null) return;
|
||||
// set background sprite if present
|
||||
if (backgroundRenderer != null && sd.fullscreen_songPicture != null)
|
||||
{
|
||||
backgroundRenderer.sprite = sd.fullscreen_songPicture;
|
||||
Color color = backgroundRenderer.color;
|
||||
color.a = 1f;
|
||||
backgroundRenderer.color = color;
|
||||
}
|
||||
|
||||
// set song name text if available
|
||||
if (songnameText != null)
|
||||
{
|
||||
songnameText.text = sd.songName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Records the elapsed play time since the session started and adds it to the song's total play time.
|
||||
/// Can be called multiple times, but only records once per session.
|
||||
/// </summary>
|
||||
public void RecordTotalPlayTime()
|
||||
{
|
||||
if (timeRecorded || currentSong == null) return;
|
||||
|
||||
float elapsed = Time.realtimeSinceStartup - sessionStartTime;
|
||||
currentSong.time_totalPlayingTime += elapsed;
|
||||
timeRecorded = true;
|
||||
|
||||
Debug.Log($"[GameManager] Recorded {elapsed:F2}s to total play time for {currentSong.songName}. New total: {currentSong.time_totalPlayingTime:F2}s");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user