Files
bansonic_beta_main/Assets/songsDatabase/selected_songInfo.cs
T

994 lines
38 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Collections;
using DG.Tweening;
public class selected_songInfo : MonoBehaviour
{
public Image t_songCoverImage;
public Image btm_songCoverImage;
[Header("Ϣ")]
public Text songNameText;
public Text currentDifficulty;
public Text sumScore_thisDifficulty;
public Text total_gameTime;
private int maxDifficulty = 22;
public Image c_DifficultyImage;
public Image c_imageMask;
[Header("ּɫ")]
public Color _0to5d5;
public Color _5d5to11;
public Color _11to16d5;
public Color _16d5to22;
public Color _equal22;
[Header("ӦѶȰť")]
public List<Button> difficultyButtons = new List<Button>();
[Header("ӦѶı")]
public List<Text> difficultyTexts = new List<Text>();
[Header("enter detail page")]
public Button enterDetailPageButton;
[Header("quick enter game play")]
public Button quickEnter_gamePlay;
[Header("ָimageɫ")]
public Sprite buttons_bgImage;
[Header("black mask")]
public Image blackMaskImage;
// store the original sprites so we don't overwrite the designer's setup
private List<Sprite> originalButtonSprites = new List<Sprite>();
[Header("Switch Animations")]
[SerializeField] bool play_SongSwitch_Anim = true;
[SerializeField] RectTransform songInfo_Root;
[SerializeField] float songSwitch_Time = 0.22f;
[SerializeField] float songSwitch_Move = 30f;
[SerializeField] Ease songSwitch_Ease = Ease.OutCubic;
[SerializeField] bool play_DifficultySwitch_Anim = true;
[SerializeField] float diffSwitch_Punch = 0.06f;
[SerializeField] float diffSwitch_Punch_Time = 0.18f;
[SerializeField] float diffSwitch_Text_Fade = 0.08f;
[SerializeField] bool switch_Use_Unscaled = true;
private CanvasGroup songInfo_Canvas;
private Vector2 songInfo_BasePos;
private bool songInfo_BaseCached;
private int lastSongId = -1;
private static void LogVerbose(string message)
{
if (GameConfig.verboseLogs) Debug.LogWarning(message);
}
void Awake()
{
LogVerbose("selected_songInfo.Awake called");
}
void OnEnable()
{
LogVerbose("selected_songInfo.OnEnable called");
// register listeners here to ensure binding even if Start wasn't called yet
if (enterDetailPageButton != null)
{
enterDetailPageButton.onClick.RemoveListener(OnEnterDetailPageClicked);
enterDetailPageButton.onClick.AddListener(OnEnterDetailPageClicked);
LogVerbose("enterDetailPageButton listener added");
}
else
{
LogVerbose("enterDetailPageButton is null in OnEnable");
}
if (quickEnter_gamePlay != null)
{
quickEnter_gamePlay.onClick.RemoveListener(OnQuickEnterClicked);
quickEnter_gamePlay.onClick.AddListener(OnQuickEnterClicked);
LogVerbose("quickEnter_gamePlay listener added");
}
else
{
LogVerbose("quickEnter_gamePlay is null in OnEnable");
}
}
void OnDisable()
{
LogVerbose("selected_songInfo.OnDisable called");
if (enterDetailPageButton != null)
{
enterDetailPageButton.onClick.RemoveListener(OnEnterDetailPageClicked);
}
if (quickEnter_gamePlay != null)
{
quickEnter_gamePlay.onClick.RemoveListener(OnQuickEnterClicked);
}
}
void OnDestroy()
{
LogVerbose("selected_songInfo.OnDestroy called");
// ensure we remove sceneLoaded subscription if still present
SceneManager.sceneLoaded -= OnSceneLoadedAfterQuickEnter;
}
void Start()
{
LogVerbose("selected_songInfo.Start called");
// ensure black mask is transparent and inactive at start
if (blackMaskImage != null)
{
var c = blackMaskImage.color;
c.a = 0f;
blackMaskImage.color = c;
blackMaskImage.gameObject.SetActive(false);
}
// capture original sprites for difficulty buttons before we make any changes
originalButtonSprites.Clear();
if (difficultyButtons != null)
{
foreach (var btn in difficultyButtons)
{
if (btn == null)
{
originalButtonSprites.Add(null);
continue;
}
var img = btn.GetComponent<Image>();
originalButtonSprites.Add(img != null ? img.sprite : null);
}
}
// attach click listeners to difficulty buttons (map list index 0..n-1 -> difficulty 0..n-1)
if (difficultyButtons != null)
{
for (int i = 0; i < difficultyButtons.Count; i++)
{
var btn = difficultyButtons[i];
if (btn == null) continue;
int idx = i; // capture
btn.onClick.RemoveAllListeners();
btn.onClick.AddListener(() => OnDifficultyButtonClicked(idx));
}
}
UpdateSelectedSongDisplay();
// Diagnostic: confirm Start ran and key refs
LogVerbose($"selected_songInfo.Start: quickEnter_gamePlay is {(quickEnter_gamePlay == null ? "NULL" : "ASSIGNED")}, enterDetail assigned={(enterDetailPageButton == null ? "NULL" : "ASSIGNED")}, blackMask assigned={(blackMaskImage == null ? "NULL" : "ASSIGNED")}");
LogVerbose($"selected_songInfo.Start: SongDataHolder.SelectedSongData is {(SongDataHolder.SelectedSongData == null ? "NULL" : SongDataHolder.SelectedSongData.songName)}");
}
public void UpdateSelectedSongDisplay()
{
if (SongDataHolder.SelectedSongData != null)
{
var song = SongDataHolder.SelectedSongData;
if (t_songCoverImage != null && song.right_pic_top_image != null)
{
t_songCoverImage.sprite = song.right_pic_top_image;
t_songCoverImage.enabled = true;
}
else
{
t_songCoverImage.enabled = false;
}
if (btm_songCoverImage != null && song.right_pic_bottom_image != null)
{
btm_songCoverImage.sprite = song.right_pic_bottom_image;
btm_songCoverImage.enabled = true;
}
else
{
btm_songCoverImage.enabled = false;
}
if (songNameText != null)
{
songNameText.text = song.songName;
}
// set difficulty button backgrounds according to currently selected difficulty in the song SO
SetDifficultyButtonBackground(song.thisLevel_selectedDifficultyID);
// Update currentDifficulty text and colored difficulty bar
float difficultyLevel = GetDifficultyLevel(song, song.thisLevel_selectedDifficultyID);
if (currentDifficulty != null)
{
currentDifficulty.text = difficultyLevel.ToString("0.0");
}
if (c_DifficultyImage != null)
{
// fill amount is ratio of difficultyLevel / maxDifficulty
float fill = Mathf.Clamp01(difficultyLevel / (float)maxDifficulty);
c_DifficultyImage.fillAmount = fill;
// choose color by ranges: left-closed, right-open, intervals of 5.5
// [0,5.5), [5.5,11), [11,16.5), [16.5,22), exactly 22 -> equal color
Color chosen = _0to5d5;
if (Mathf.Approximately(difficultyLevel, maxDifficulty))
{
chosen = _equal22;
}
else if (difficultyLevel >= 16.5f)
{
chosen = _16d5to22;
}
else if (difficultyLevel >= 11f)
{
chosen = _11to16d5;
}
else if (difficultyLevel >= 5.5f)
{
chosen = _5d5to11;
}
else if (difficultyLevel >= 0)
{
chosen = _0to5d5;
}
else
{
chosen = Color.white;
}
// ensure color is opaque
chosen.a = 1f;
// apply color to the mask image if available, otherwise fall back to the difficulty image
if (c_imageMask != null)
{
c_imageMask.color = chosen;
}
else
{
c_DifficultyImage.color = chosen;
}
}
// Update sumScore_thisDifficulty based on currently selected difficulty
if (sumScore_thisDifficulty != null)
{
int sel = song.thisLevel_selectedDifficultyID;
int totalForDifficulty = 0;
// Try to find ChartFileEntry for this difficulty
if (song.chartFiles != null)
{
foreach (var entry in song.chartFiles)
{
if (entry != null && entry.difficulty == sel)
{
if (entry.totalPersonalRecordForThisDifficulty > 0)
{
totalForDifficulty = entry.totalPersonalRecordForThisDifficulty;
}
else
{
int p = song.GetPersonalRecord(sel);
int i = song.GetIdolRecord(sel);
totalForDifficulty = p + i;
}
break;
}
}
}
// fallback to maps if chartFiles did not contain entry
if (totalForDifficulty == 0)
{
int p = song.GetPersonalRecord(sel);
int i = song.GetIdolRecord(sel);
totalForDifficulty = p + i;
}
sumScore_thisDifficulty.text = totalForDifficulty.ToString();
}
// Update total_gameTime once from SongData.game_enterTimes (no dynamic change on difficulty switch)
if (total_gameTime != null)
{
int enterTimes = song.game_enterTimes;
total_gameTime.text = enterTimes.ToString();
}
TryPlaySongSwitchAnim(song.songID);
}
else
{
// Clear display if no song selected
if (t_songCoverImage != null)
{
t_songCoverImage.sprite = null;
t_songCoverImage.enabled = false;
}
if (btm_songCoverImage != null)
{
btm_songCoverImage.sprite = null;
btm_songCoverImage.enabled = false;
}
if (songNameText != null)
{
songNameText.text = "δѡ";
}
// restore original difficulty button backgrounds and text colors
SetDifficultyButtonBackground(-1);
if (sumScore_thisDifficulty != null) sumScore_thisDifficulty.text = "0";
if (total_gameTime != null) total_gameTime.text = "0";
lastSongId = -1;
}
}
private void EnsureOriginalSpritesCount()
{
if (difficultyButtons == null) return;
while (originalButtonSprites.Count < difficultyButtons.Count)
{
var btn = difficultyButtons[originalButtonSprites.Count];
if (btn == null) originalButtonSprites.Add(null);
else
{
var img = btn.GetComponent<Image>();
originalButtonSprites.Add(img != null ? img.sprite : null);
}
}
}
private void SetDifficultyButtonBackground(int selectedDifficulty)
{
if (difficultyButtons == null || difficultyButtons.Count == 0) return;
EnsureOriginalSpritesCount();
for (int i = 0; i < difficultyButtons.Count; i++)
{
var btn = difficultyButtons[i];
if (btn == null) continue;
var img = btn.GetComponent<Image>();
if (img == null) continue;
if (i == selectedDifficulty)
{
// apply highlight sprite to selected button
if (buttons_bgImage != null)
{
img.sprite = buttons_bgImage;
img.color = Color.white;
}
// set corresponding text (if exists) to white
if (difficultyTexts != null && i < difficultyTexts.Count && difficultyTexts[i] != null)
{
difficultyTexts[i].color = Color.white;
}
}
else
{
// restore original sprite if we recorded one; otherwise leave as-is
if (i < originalButtonSprites.Count)
{
img.sprite = originalButtonSprites[i];
}
// set corresponding text (if exists) to black
if (difficultyTexts != null && i < difficultyTexts.Count && difficultyTexts[i] != null)
{
difficultyTexts[i].color = Color.black;
}
}
}
}
private void OnDifficultyButtonClicked(int index)
{
// map button index 0..3 to song SO difficulty 0..3
if (SongDataHolder.SelectedSongData != null)
{
SongDataHolder.SelectedSongData.thisLevel_selectedDifficultyID = Mathf.Clamp(index, 0, 3);
// immediately update UI to reflect new selection
SetDifficultyButtonBackground(SongDataHolder.SelectedSongData.thisLevel_selectedDifficultyID);
// refresh displayed song info so currentDifficulty / difficulty bar update to new difficulty
UpdateSelectedSongDisplay();
PlayDifficultySwitchAnim(index);
}
else
{
Debug.LogWarning("No song selected - difficulty button click ignored.");
}
}
private void EnsureSongInfoRoot()
{
if (songInfo_Root == null)
{
songInfo_Root = transform as RectTransform;
}
if (songInfo_Root == null) return;
if (songInfo_Canvas == null || songInfo_Canvas.gameObject != songInfo_Root.gameObject)
{
songInfo_Canvas = songInfo_Root.GetComponent<CanvasGroup>();
if (songInfo_Canvas == null)
{
songInfo_Canvas = songInfo_Root.gameObject.AddComponent<CanvasGroup>();
}
}
}
private void TryPlaySongSwitchAnim(int songId)
{
if (!play_SongSwitch_Anim)
{
lastSongId = songId;
return;
}
if (lastSongId == songId)
{
return;
}
lastSongId = songId;
EnsureSongInfoRoot();
if (songInfo_Root == null || songInfo_Canvas == null)
{
return;
}
songInfo_BasePos = songInfo_Root.anchoredPosition;
songInfo_BaseCached = true;
songInfo_Root.DOKill();
songInfo_Canvas.DOKill();
songInfo_Root.anchoredPosition = songInfo_BasePos + new Vector2(-songSwitch_Move, 0f);
songInfo_Canvas.alpha = 0f;
Sequence seq = DOTween.Sequence().SetUpdate(switch_Use_Unscaled);
seq.Join(songInfo_Root.DOAnchorPos(songInfo_BasePos, songSwitch_Time).SetEase(songSwitch_Ease));
seq.Join(songInfo_Canvas.DOFade(1f, songSwitch_Time));
}
private void PlayDifficultySwitchAnim(int index)
{
if (!play_DifficultySwitch_Anim)
{
return;
}
if (difficultyButtons != null && index >= 0 && index < difficultyButtons.Count)
{
var btn = difficultyButtons[index];
if (btn != null)
{
Transform t = btn.transform;
t.DOKill();
t.localScale = Vector3.one;
t.DOPunchScale(Vector3.one * diffSwitch_Punch, diffSwitch_Punch_Time, 6, 0.7f)
.SetUpdate(switch_Use_Unscaled);
}
}
FlashGraphic(currentDifficulty);
FlashGraphic(sumScore_thisDifficulty);
if (c_DifficultyImage != null)
{
FlashGraphic(c_DifficultyImage);
}
}
private void FlashGraphic(Graphic g)
{
if (g == null) return;
g.DOKill();
Color baseColor = g.color;
float baseAlpha = baseColor.a <= 0f ? 1f : baseColor.a;
baseColor.a = baseAlpha;
g.color = baseColor;
Sequence seq = DOTween.Sequence().SetUpdate(switch_Use_Unscaled);
seq.Append(g.DOFade(Mathf.Clamp01(baseAlpha * 0.35f), diffSwitch_Text_Fade));
seq.Append(g.DOFade(baseAlpha, diffSwitch_Text_Fade));
}
public void OnEnterDetailPageClicked()
{
if (SongDataHolder.SelectedSongData != null)
{
StartCoroutine(LoadSceneAsync("Songs_Select"));
}
else
{
Debug.LogWarning("No song selected, cannot enter detail page.");
}
}
private IEnumerator LoadSceneAsync(string sceneName)
{
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
while (!asyncLoad.isDone)
{
yield return null;
}
}
private void OnQuickEnterClicked()
{
// use SongData from holder or SongButton; ensure selection exists
SongData sd = FindSongDataFromSelectedButton();
if (sd == null) sd = SongDataHolder.SelectedSongData;
if (sd == null)
{
LogVerbose("No song selected - cannot quick enter gameplay.");
return;
}
// Validate that the selected difficulty has a chart file before proceeding
var chart = sd.GetChartFile(sd.thisLevel_selectedDifficultyID);
if (chart == null)
{
Debug.LogError($"QuickEnter aborted: Song '{sd.songName}' has no chart file for difficulty {sd.thisLevel_selectedDifficultyID}.");
return;
}
LogVerbose($"QuickEnter clicked, selected song: {sd.songName} id={sd.songID} difficulty={sd.thisLevel_selectedDifficultyID}");
// pass selection to BeatmapManager pending fields so newly loaded scene picks it up
BeatmapManager.SetPendingSong(sd, sd.thisLevel_selectedDifficultyID);
// keep selected in holder to make it accessible in new scene
SongDataHolder.SelectedSongData = sd;
// mark this object to persist across scene load so callbacks/coroutines are valid
DontDestroyOnLoad(this.gameObject);
// start fade and load sequence
StartCoroutine(QuickEnterSequence());
}
private IEnumerator QuickEnterSequence()
{
LogVerbose("QuickEnterSequence started: beginning fade");
// Fade black mask in over 0.25 seconds
float duration = 0.25f;
if (blackMaskImage != null)
{
// if mask is inactive, ensure its alpha is zero before activating
if (!blackMaskImage.gameObject.activeSelf)
{
Color cc = blackMaskImage.color;
cc.a = 0f;
blackMaskImage.color = cc;
blackMaskImage.gameObject.SetActive(true);
}
else
{
// active: explicitly set alpha to 0 to start fade from transparent
Color cc = blackMaskImage.color;
cc.a = 0f;
blackMaskImage.color = cc;
}
float t = 0f;
Color c = blackMaskImage.color;
while (t < duration)
{
t += Time.deltaTime;
float alpha = Mathf.Clamp01(t / duration);
c.a = alpha;
blackMaskImage.color = c;
yield return null;
}
c.a = 1f;
blackMaskImage.color = c;
}
LogVerbose("Fade complete, loading gameplay scene...");
// after black screen, load gameplay scene asynchronously and wait for completion
SceneManager.sceneLoaded += OnSceneLoadedAfterQuickEnter;
var asyncOp = SceneManager.LoadSceneAsync("gamePlay_gamePlay");
if (asyncOp != null)
{
// wait until load completes
while (!asyncOp.isDone)
{
yield return null;
}
}
else
{
// fallback to synchronous load
SceneManager.LoadScene("gamePlay_gamePlay");
}
yield break;
}
private SongData FindSongDataFromSelectedButton()
{
var allButtons = Object.FindObjectsByType<SongButton>(FindObjectsInactive.Exclude, FindObjectsSortMode.InstanceID);
LogVerbose($"FindSongDataFromSelectedButton: found {allButtons.Length} SongButton instances");
foreach (var sb in allButtons)
{
if (sb == null) continue;
try
{
// log selection marker info for each button to help debugging
var field = sb.GetType().GetField("selected_boarder", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
Image img = null;
if (field != null) img = field.GetValue(sb) as Image;
float alpha = img != null ? img.color.a : -1f;
LogVerbose($"SongButton id field value for '{sb.name}': selected_boarder alpha={alpha}");
if (img != null && img.color.a > 0.5f)
{
// prefer thisSong_so if present
var soField = sb.GetType().GetField("thisSong_so", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
if (soField != null)
{
var soObj = soField.GetValue(sb);
if (soObj != null && soObj is SongData)
{
LogVerbose($"Selected SongButton has thisSong_so assigned: {((SongData)soObj).songName}");
return soObj as SongData;
}
}
// fallback: use song id on SongButton
var idField = sb.GetType().GetField("song_songSerialID", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
if (idField != null)
{
int id = (int)idField.GetValue(sb);
LogVerbose($"Selected SongButton id = {id}");
var sd = SongDataLibrary.Instance != null ? SongDataLibrary.Instance.GetSongDataByID(id) : null;
if (sd != null)
{
LogVerbose($"Found SongData by id: {sd.songName}");
return sd;
}
}
}
}
catch (System.Exception ex)
{
LogVerbose("Exception while inspecting SongButton: " + ex.Message);
}
}
LogVerbose("FindSongDataFromSelectedButton: no selected SongButton found");
return null;
}
private void OnSceneLoadedAfterQuickEnter(Scene scene, LoadSceneMode mode)
{
// If this object has been destroyed, unsubscribe and bail out safely
if (!this)
{
SceneManager.sceneLoaded -= OnSceneLoadedAfterQuickEnter;
return;
}
// only handle target gameplay scene
if (scene.name != "gamePlay_gamePlay")
{
// unsubscribe anyway
SceneManager.sceneLoaded -= OnSceneLoadedAfterQuickEnter;
return;
}
// If the black mask reference exists but the GameObject is disabled, ensure its alpha is 0
if (blackMaskImage != null && !blackMaskImage.gameObject.activeSelf)
{
var cc = blackMaskImage.color;
cc.a = 0f;
blackMaskImage.color = cc;
}
// perform setup: load chart JSON from selected SongData's selected difficulty and set audio
SongData selected = null;
// Prefer SongButton's assigned SO if available
selected = FindSongDataFromSelectedButton();
// Fallback to SongDataHolder
if (selected == null) selected = SongDataHolder.SelectedSongData;
if (selected == null)
{
LogVerbose("OnSceneLoadedAfterQuickEnter: SelectedSongData is null");
SceneManager.sceneLoaded -= OnSceneLoadedAfterQuickEnter;
// allow this object to be destroyed now
Destroy(this.gameObject);
return;
}
// find managers in the new scene
var beatmapManager = Object.FindAnyObjectByType<BeatmapManager>();
var gameManager = Object.FindAnyObjectByType<GameManager>();
LogVerbose($"selected song: {selected.songName} (id {selected.songID}), difficulty {selected.thisLevel_selectedDifficultyID}");
// get chart TextAsset from SongData for current selected difficulty
TextAsset chartAsset = selected.GetChartFile(selected.thisLevel_selectedDifficultyID);
if (chartAsset == null)
{
LogVerbose($"Chart file for difficulty {selected.thisLevel_selectedDifficultyID} not found on SongData {selected.songName}");
}
else
{
LogVerbose($"Chart asset found, size={chartAsset.text?.Length ?? 0} chars");
}
// parse beatmap only and assign music, but DO NOT start spawning until player starts
if (beatmapManager != null && chartAsset != null)
{
LogVerbose("Parsing chart JSON via BeatmapManager.ParseJsonOnly");
bool parsed = false;
try
{
parsed = beatmapManager.ParseJsonOnly(chartAsset.text);
}
catch (System.Exception ex)
{
LogVerbose("Exception during ParseJsonOnly: " + ex.Message + "\n" + ex.StackTrace);
}
LogVerbose($"ParseJsonOnly returned {parsed}");
if (!parsed)
{
LogVerbose("Failed to parse chart JSON from SongData chart file.");
}
else
{
LogVerbose($"Beatmap parsed: beatmap is {(beatmapManager.beatmap != null ? "NOT null" : "null")}, parsedMusicFile={beatmapManager.parsedMusicFile}, globalDelaySeconds={beatmapManager.globalDelaySeconds}");
}
}
else
{
if (beatmapManager == null) LogVerbose("BeatmapManager not found in gameplay scene");
}
// assign audio clip to GameManager.musicSource if available
if (gameManager != null)
{
if (selected.audioFile != null && gameManager.musicSource != null)
{
gameManager.musicSource.clip = selected.audioFile;
gameManager.musicSource.loop = false;
// make sure AudioSource won't auto-play due to inspector settings
gameManager.musicSource.playOnAwake = false;
// warm up audio by playing muted so decoding happens before unpause
try
{
gameManager.musicSource.mute = true;
gameManager.musicSource.Play();
Debug.LogWarning("Warmed audio playback (muted) after assigning SongData.audioFile");
}
catch (System.Exception ex)
{
Debug.LogWarning("Failed to Play() for warmup: " + ex.Message);
}
LogVerbose("Assigned SongData.audioFile to GameManager.musicSource.clip (playOnAwake disabled)");
// Request PauseManager-driven pause (centralized)
RequestPauseManagerPause();
}
else
{
// try to load by parsedMusicFile similar to pressStart normal mode
if (gameManager.musicSource != null && beatmapManager != null && !string.IsNullOrEmpty(beatmapManager.parsedMusicFile))
{
LogVerbose($"Attempting Resources.Load for audio: {beatmapManager.parsedMusicFile}");
var ac = Resources.Load<AudioClip>(beatmapManager.parsedMusicFile);
if (ac != null)
{
gameManager.musicSource.clip = ac;
gameManager.musicSource.playOnAwake = false;
// warm up audio by playing muted
try
{
gameManager.musicSource.mute = true;
gameManager.musicSource.Play();
LogVerbose("Warmed audio playback (muted) after Resources.Load");
}
catch (System.Exception ex)
{
LogVerbose("Failed to Play() for warmup after Resources.Load: " + ex.Message);
}
LogVerbose("Loaded audio via Resources.Load(parsedMusicFile) and disabled playOnAwake");
RequestPauseManagerPause();
}
else
{
LogVerbose($"Resources.Load failed for '{beatmapManager.parsedMusicFile}'");
}
}
else
{
LogVerbose("No audio assigned: selected.audioFile null and parsedMusicFile empty or musicSource missing");
}
}
}
// Ensure PauseManager pause is requested; the RequestPauseManagerPause call above will have already
// attempted to pause the game if audio was set. If not yet requested, call it now to reuse PauseManager.
RequestPauseManagerPause();
// Start coroutine to wait for player input (Space) to begin playback and spawning
// Use a safe host for coroutine in case this MonoBehaviour is destroyed unexpectedly
var pauseMgr = PauseManager.Instance != null ? PauseManager.Instance : Object.FindAnyObjectByType<PauseManager>();
MonoBehaviour coroutineHost = (pauseMgr as MonoBehaviour) ?? (gameManager as MonoBehaviour) ?? (beatmapManager as MonoBehaviour) ?? this;
if (coroutineHost != null)
{
LogVerbose("Starting WaitForPlayerStartAndBegin coroutine on host: " + coroutineHost.name);
coroutineHost.StartCoroutine(WaitForPlayerStartAndBegin(beatmapManager, gameManager));
}
else
{
LogVerbose("No valid coroutine host found to start WaitForPlayerStartAndBegin.");
}
// unsubscribe from sceneLoaded
SceneManager.sceneLoaded -= OnSceneLoadedAfterQuickEnter;
}
private IEnumerator WaitForPlayerStartAndBegin(BeatmapManager beatmapManager, GameManager gameManager)
{
LogVerbose("Waiting for player to press Space to start playback...");
// Wait until player presses Space
while (!Input.GetKeyDown(KeyCode.Space))
{
yield return null;
}
LogVerbose("Player pressed Space. Preparing to start playback.");
// wait for NotePool prewarm to complete (block until finished to avoid hitch)
if (NotePool.Instance != null)
{
LogVerbose("Waiting for NotePool prewarm to finish (blocking)...");
while (!NotePool.Instance.IsPrewarmed)
{
yield return null;
}
LogVerbose("NotePool prewarm completed.");
}
// ensure audio has been warmed (attempt play muted if not already playing)
if (gameManager != null && gameManager.musicSource != null && gameManager.musicSource.clip != null && !gameManager.musicSource.isPlaying)
{
try
{
gameManager.musicSource.mute = true;
gameManager.musicSource.Play();
}
catch { }
}
// Prewarm particle systems via AnimationController to avoid first-play hitch
var anim = AnimationController.Global != null ? AnimationController.Global : Object.FindAnyObjectByType<AnimationController>();
if (anim != null)
{
LogVerbose("Starting AnimationController particle prewarm (will wait until complete)...");
// yield until the prewarm completes
yield return StartCoroutine(anim.PrewarmParticlesRoutine(2));
LogVerbose("AnimationController particle prewarm complete.");
}
// Unpause via PauseManager
var pauseMgr = PauseManager.Instance != null ? PauseManager.Instance : Object.FindAnyObjectByType<PauseManager>();
if (pauseMgr != null) pauseMgr.Pause(false);
// small buffer to avoid hitching immediately after unpause: wait configured delay from GameManager
// Use scaled WaitForSeconds so PauseManager (Escape) will pause the countdown
float unpauseBuffer = 3f;
if (gameManager != null) unpauseBuffer = Mathf.Max(0f, gameManager.playbackStartDelay);
yield return new WaitForSeconds(unpauseBuffer);
// Start spawning notes
if (beatmapManager != null && beatmapManager.beatmap != null)
{
LogVerbose("Calling beatmapManager.LoadBeatmap");
beatmapManager.LoadBeatmap(beatmapManager.beatmap);
}
else
{
LogVerbose("Cannot start beatmap: beatmapManager or beatmap is null.");
}
// fade out black mask in gameManager if available
if (gameManager != null)
{
gameManager.StartCoroutine(gameManager.FadeOutBlackMask(0.25f));
}
// Play audio using GameManager.musicSource with delay
if (gameManager != null && gameManager.musicSource != null)
{
float delay = beatmapManager != null ? beatmapManager.globalDelaySeconds : 0f;
if (gameManager.musicSource.clip != null)
{
LogVerbose($"Starting audio playback with delay {delay}");
// Use GameManager helper to ensure unmute and start playback reliably
try { gameManager.PlayMusicWithDelay(delay); }
catch (System.Exception ex) { LogVerbose("Failed to start music via GameManager.PlayMusicWithDelay: " + ex); }
}
else
{
LogVerbose("GameManager.musicSource.clip is null; skipping audio playback.");
}
}
// done with this helper object - allow it to be destroyed
Destroy(this.gameObject);
yield break;
}
// Debug helper: press F1 at runtime to print diagnostic state to Console (warnings)
void Update()
{
if (Input.GetKeyDown(KeyCode.F1))
{
Debug.LogWarning("selected_songInfo.DebugTrigger: F1 pressed - dumping diagnostic info");
Debug.LogWarning($"GameObject activeInHierarchy={gameObject.activeInHierarchy}, enabled={enabled}");
Debug.LogWarning($"quickEnter_gamePlay assigned={(quickEnter_gamePlay==null?"NULL":"ASSIGNED")}");
Debug.LogWarning($"enterDetailPageButton assigned={(enterDetailPageButton==null?"NULL":"ASSIGNED")}");
Debug.LogWarning($"blackMaskImage assigned={(blackMaskImage==null?"NULL":"ASSIGNED")}");
Debug.LogWarning($"SongDataHolder.SelectedSongData={(SongDataHolder.SelectedSongData==null?"NULL":SongDataHolder.SelectedSongData.songName)}");
var allButtons = Object.FindObjectsByType<SongButton>(FindObjectsInactive.Exclude, FindObjectsSortMode.InstanceID);
Debug.LogWarning($"Found {allButtons.Length} SongButton instances in scene");
for (int i = 0; i < allButtons.Length; i++)
{
var sb = allButtons[i];
if (sb == null) continue;
Debug.LogWarning($" [{i}] name={sb.name} active={sb.gameObject.activeInHierarchy}");
}
}
}
// Helper to centralize PauseManager pause request so selected_songInfo reuses PauseManager implementation
private bool pauseRequested = false;
private void RequestPauseManagerPause()
{
if (pauseRequested) return;
var pm = PauseManager.Instance != null ? PauseManager.Instance : Object.FindAnyObjectByType<PauseManager>();
if (pm != null)
{
pm.Pause(true);
LogVerbose("selected_songInfo: PauseManager.Pause(true) requested (centralized)");
pauseRequested = true;
}
else
{
LogVerbose("selected_songInfo: PauseManager not found when requesting pause");
}
}
// Helper to get difficulty level (float) from SongData for a given difficulty id
private float GetDifficultyLevel(SongData song, int difficultyId)
{
if (song == null) return 0f;
if (song.chartFiles != null)
{
foreach (var entry in song.chartFiles)
{
if (entry != null && entry.difficulty == difficultyId)
{
return entry.difficultyLEVEL;
}
}
}
// fallback: try dictionary if available
if (song.difficultyNumberMap != null && song.difficultyNumberMap.ContainsKey(difficultyId))
{
return (float)song.difficultyNumberMap[difficultyId];
}
return 0f;
}
private string FormatSeconds(int totalSeconds)
{
if (totalSeconds <= 0) return "0:00";
int hours = totalSeconds / 3600;
int minutes = (totalSeconds % 3600) / 60;
int seconds = totalSeconds % 60;
if (hours > 0)
return string.Format("{0}:{1:D2}:{2:D2}", hours, minutes, seconds);
return string.Format("{0}:{1:D2}", minutes, seconds);
}
}