Files
2026-06-25 11:48:56 +08:00

267 lines
7.4 KiB
C#
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
public class SongDataLibrary : MonoBehaviour
{
public static SongDataLibrary Instance;
private Dictionary<int, SongData> songDataDictionary = new Dictionary<int, SongData>();
private Coroutine loadCoroutine;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void EnsureRuntimeInstance()
{
if (Instance != null) return;
if (SceneObjectLookupCache.FindAny<SongDataLibrary>() != null) return;
var go = new GameObject(nameof(SongDataLibrary));
Object.DontDestroyOnLoad(go);
go.AddComponent<SongDataLibrary>();
}
// Documentation text normalized.
// Documentation text normalized.
public string[] subfolderNames;
[Header("Song List")]
[SerializeField] SongList songList;
[SerializeField] bool useSongListIfAvailable = true;
[Header("Async Load Settings")]
[SerializeField] int yieldEvery = 16;
[SerializeField] bool logLoadProgress = false;
public bool IsLoaded { get; private set; }
public bool IsLoading { get; private set; }
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
BeginLoad();
}
else
{
Destroy(gameObject);
}
}
private IEnumerator LoadSongDataCoroutine()
{
if (IsLoading || IsLoaded)
{
loadCoroutine = null;
yield break;
}
IsLoading = true;
IsLoaded = false;
songDataDictionary.Clear();
// Allow one frame to render before heavy load begins
yield return null;
// prefer SongList if available to avoid heavy Resources.LoadAll
if (useSongListIfAvailable)
{
if (songList == null)
{
SongSelectUI ui = SceneObjectLookupCache.FindAny<SongSelectUI>();
if (ui != null)
{
songList = ui.songList;
}
}
if (songList != null && songList.songs != null && songList.songs.Count > 0)
{
yield return LoadFromList(songList.songs, "songList");
yield return LoadFromRuntimeRegistry("runtimeSongs");
IsLoaded = true;
IsLoading = false;
loadCoroutine = null;
yield break;
}
}
yield return LoadFromPath("song_songIndex", "root");
if (subfolderNames != null)
{
for (int i = 0; i < subfolderNames.Length; i++)
{
string subfolder = subfolderNames[i];
if (string.IsNullOrEmpty(subfolder))
{
continue;
}
yield return LoadFromPath("song_songIndex/" + subfolder, subfolder);
// yield a frame between folders to reduce hitch
yield return null;
}
}
IsLoaded = true;
IsLoading = false;
loadCoroutine = null;
}
private IEnumerator LoadFromPath(string path, string label)
{
SongData[] assets = RuntimeResourcesCache.LoadSongsFromPath(path);
if (assets == null || assets.Length == 0)
{
if (logLoadProgress)
{
Debug.Log("SongDataLibrary: no assets found at '" + path + "'");
}
yield break;
}
int yielded = 0;
foreach (SongData data in assets)
{
if (data == null)
{
continue;
}
if (!songDataDictionary.ContainsKey(data.songID))
{
songDataDictionary.Add(data.songID, data);
if (logLoadProgress)
{
Debug.Log("Loaded SongData [" + label + "] songID: " + data.songID + ", name: " + data.songName);
}
}
else if (logLoadProgress)
{
Debug.LogWarning("Duplicate songID: " + data.songID);
}
yielded++;
if (yieldEvery > 0 && (yielded % yieldEvery) == 0)
{
// yield to spread work across frames
yield return null;
}
}
}
private IEnumerator LoadFromList(List<SongData> list, string label)
{
if (list == null || list.Count == 0)
{
yield break;
}
int yielded = 0;
for (int i = 0; i < list.Count; i++)
{
SongData data = list[i];
if (data == null) continue;
if (!songDataDictionary.ContainsKey(data.songID))
{
songDataDictionary.Add(data.songID, data);
if (logLoadProgress)
{
Debug.Log("Loaded SongData [" + label + "] songID: " + data.songID + ", name: " + data.songName);
}
}
else if (logLoadProgress)
{
Debug.LogWarning("Duplicate songID: " + data.songID);
}
yielded++;
if (yieldEvery > 0 && (yielded % yieldEvery) == 0)
{
yield return null;
}
}
}
private IEnumerator LoadFromRuntimeRegistry(string label)
{
SongData[] runtimeSongs = DlcRuntimeRegistry.GetAllSongs();
if (runtimeSongs == null || runtimeSongs.Length == 0)
{
yield break;
}
int yielded = 0;
for (int i = 0; i < runtimeSongs.Length; i++)
{
SongData data = runtimeSongs[i];
if (data == null)
{
continue;
}
if (!songDataDictionary.ContainsKey(data.songID))
{
songDataDictionary.Add(data.songID, data);
if (logLoadProgress)
{
Debug.Log("Loaded SongData [" + label + "] songID: " + data.songID + ", name: " + data.songName);
}
}
else if (logLoadProgress)
{
Debug.LogWarning("Duplicate songID: " + data.songID);
}
yielded++;
if (yieldEvery > 0 && (yielded % yieldEvery) == 0)
{
yield return null;
}
}
}
public SongData GetSongDataByID(int songID)
{
if (songDataDictionary.ContainsKey(songID))
{
SongData data = songDataDictionary[songID];
data.EnsurePersistentDataLoaded();
return data;
}
Debug.LogError("未找到 songID: " + songID);
return null;
}
public List<SongData> GetAllSongs()
{
return new List<SongData>(songDataDictionary.Values);
}
public void RefreshLibrary()
{
if (!isActiveAndEnabled)
{
return;
}
if (loadCoroutine != null)
{
StopCoroutine(loadCoroutine);
loadCoroutine = null;
}
IsLoaded = false;
IsLoading = false;
songDataDictionary.Clear();
BeginLoad();
}
private void BeginLoad()
{
if (loadCoroutine != null)
{
StopCoroutine(loadCoroutine);
}
loadCoroutine = StartCoroutine(LoadSongDataCoroutine());
}
}