267 lines
7.4 KiB
C#
267 lines
7.4 KiB
C#
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());
|
||
}
|
||
}
|