feat: Unified ResourceLoader

- 新增 统一 `ResourceLoader`类,负责同步/异步加载资源,并对已加载的资源缓存。
- 修改 `MusicTransition`, `AudioSystem`, `LongAudioContainerPlayer`, `MusicSegment`, 'SfxSystem`, `HapticSystem`使用最新的`ResourceLoader`。
This commit is contained in:
2026-05-14 11:35:38 +08:00
parent 36f4c2e2d3
commit c30239111c
7 changed files with 116 additions and 7 deletions
@@ -18,6 +18,8 @@ namespace OCES.Audio
public IReadOnlyDictionary<Type, Enum> ActiveStates { get; private set; } public IReadOnlyDictionary<Type, Enum> ActiveStates { get; private set; }
public enum ConsoleLogLevel { Off, Log, Warning, Error } //TODO 实现这个功能 public enum ConsoleLogLevel { Off, Log, Warning, Error } //TODO 实现这个功能
internal ResourceLoader ResourceLoader;
const string k_audioConfigPath = "AudioData"; const string k_audioConfigPath = "AudioData";
const string k_audioResourcePath = "Audios"; const string k_audioResourcePath = "Audios";
@@ -29,6 +31,7 @@ namespace OCES.Audio
AudioMixer m_mixer; AudioMixer m_mixer;
Tween m_lowpassTween; Tween m_lowpassTween;
// ───────────────────────────────────────────── // ─────────────────────────────────────────────
// 公开接口 // 公开接口
// ───────────────────────────────────────────── // ─────────────────────────────────────────────
@@ -280,7 +283,9 @@ namespace OCES.Audio
Instance = this; Instance = this;
DontDestroyOnLoad(gameObject); DontDestroyOnLoad(gameObject);
this.m_mixer = Resources.Load<AudioMixer>("Audios/Master"); this.ResourceLoader = gameObject.AddComponent<ResourceLoader>();
this.m_mixer = this.ResourceLoader.LoadSync<AudioMixer>("Audios/Master");
// ── SFX 调度器 ── // ── SFX 调度器 ──
// AudioSystem.cs 中 // AudioSystem.cs 中
@@ -409,9 +414,13 @@ namespace OCES.Audio
public static T Load<T>(string tableName) where T : IBinarySerializable, new() public static T Load<T>(string tableName) where T : IBinarySerializable, new()
{ {
TextAsset bytes = Resources.Load<TextAsset>(tableName); TextAsset bytes = AudioSystem.Instance.ResourceLoader.LoadSync<TextAsset>(tableName);
if (!bytes) if (!bytes)
{
Debug.LogError($"未找到表 {tableName}"); Debug.LogError($"未找到表 {tableName}");
return default;
}
IBinarySerializable data = new T(); IBinarySerializable data = new T();
bool readOk = FileManager.ReadBinaryDataFromBytes(bytes.bytes, ref data); bool readOk = FileManager.ReadBinaryDataFromBytes(bytes.bytes, ref data);
if (readOk) if (readOk)
@@ -340,7 +340,12 @@ namespace OCES.Audio
return null; return null;
} }
AudioClip clip = Resources.Load<AudioClip>($"Audios/{segment.Name}"); // AudioClip clip = Resources.Load<AudioClip>($"Audios/{segment.Name}");
AudioClip clip = null;
AudioSystem.Instance.ResourceLoader.LoadAsync<AudioClip>($"Audios/{segment.Name}", loadedClip =>
{
clip = loadedClip;
});
if (!clip) if (!clip)
{ {
Debug.LogError($"[LongAudioContainerPlayer] 音频文件未找到: {segment.Name}"); Debug.LogError($"[LongAudioContainerPlayer] 音频文件未找到: {segment.Name}");
@@ -16,7 +16,7 @@ namespace OCES.Audio
{ {
foreach (MusicSegment segment in this.m_musicSegmentInfos.Values) foreach (MusicSegment segment in this.m_musicSegmentInfos.Values)
{ {
AudioClip clip = Resources.Load<AudioClip>($"Audios/{segment.Name}"); AudioClip clip = AudioSystem.Instance.ResourceLoader.LoadSync<AudioClip>($"Audios/{segment.Name}");
if (!clip) if (!clip)
{ {
Debug.LogError($"[MusicSegmentConfig] 音频文件未找到: {segment.Name}, SegmentId: {segment.Id}"); Debug.LogError($"[MusicSegmentConfig] 音频文件未找到: {segment.Name}, SegmentId: {segment.Id}");
@@ -405,7 +405,12 @@ namespace OCES.Audio
bool SetupSource(AudioSource source, ActiveSound activeSound, int clipIndex = 0) bool SetupSource(AudioSource source, ActiveSound activeSound, int clipIndex = 0)
{ {
AudioObject audioObject = activeSound.AudioObject; AudioObject audioObject = activeSound.AudioObject;
AudioClip clip = Resources.Load<AudioClip>($"Audios/{audioObject.Name[clipIndex]}"); // TODO 抽象同一资源加载接口 //AudioClip clip = Resources.Load<AudioClip>($"Audios/{audioObject.Name[clipIndex]}"); // TODO 抽象同一资源加载接口
AudioClip clip = null;
AudioSystem.Instance.ResourceLoader.LoadAsync<AudioClip>($"Audios/{audioObject.Name[clipIndex]}", loadedClip =>
{
clip = loadedClip;
});
if (!clip) if (!clip)
{ {
Debug.LogError($"音频文件未找到:{audioObject.Name[clipIndex]}"); Debug.LogError($"音频文件未找到:{audioObject.Name[clipIndex]}");
@@ -14,6 +14,8 @@ namespace OCES.Haptic
[NonSerialized] [NonSerialized]
public bool IsMeetsAdvanceRequirements; public bool IsMeetsAdvanceRequirements;
internal ResourceLoader ResourceLoader;
HapticObjectConfig m_hapticObjects; HapticObjectConfig m_hapticObjects;
const string k_hapticConfigPath = "HapticData/"; const string k_hapticConfigPath = "HapticData/";
const string k_hapticResourcesPath = "Haptics/"; const string k_hapticResourcesPath = "Haptics/";
@@ -111,6 +113,8 @@ namespace OCES.Haptic
Instance = this; Instance = this;
DontDestroyOnLoad(gameObject); DontDestroyOnLoad(gameObject);
this.ResourceLoader = gameObject.AddComponent<ResourceLoader>();
this.m_hapticObjects = HapticConfigLoader.Load<HapticObjectConfig>(k_hapticConfigPath + "HapticObject"); this.m_hapticObjects = HapticConfigLoader.Load<HapticObjectConfig>(k_hapticConfigPath + "HapticObject");
this.IsHapticSupported = DeviceCapabilities.isVersionSupported; this.IsHapticSupported = DeviceCapabilities.isVersionSupported;
this.IsMeetsAdvanceRequirements = DeviceCapabilities.meetsAdvancedRequirements; this.IsMeetsAdvanceRequirements = DeviceCapabilities.meetsAdvancedRequirements;
@@ -118,16 +122,19 @@ namespace OCES.Haptic
static HapticClip GetHapticClip(HapticObject hapticObject) static HapticClip GetHapticClip(HapticObject hapticObject)
{ {
return Resources.Load<HapticClip>(k_hapticResourcesPath + hapticObject.Payload); return Instance.ResourceLoader.LoadSync<HapticClip>(k_hapticResourcesPath + hapticObject.Payload);
} }
static class HapticConfigLoader static class HapticConfigLoader
{ {
internal static T Load<T>(string tableName) where T : IBinarySerializable, new() internal static T Load<T>(string tableName) where T : IBinarySerializable, new()
{ {
TextAsset bytes = Resources.Load<TextAsset>(tableName); TextAsset bytes = Instance.ResourceLoader.LoadSync<TextAsset>(tableName);
if (!bytes) if (!bytes)
{
Debug.LogError($"未找到表 {tableName}"); Debug.LogError($"未找到表 {tableName}");
return default;
}
IBinarySerializable data = new T(); IBinarySerializable data = new T();
bool readOk = FileManager.ReadBinaryDataFromBytes(bytes.bytes, ref data); bool readOk = FileManager.ReadBinaryDataFromBytes(bytes.bytes, ref data);
if (readOk) if (readOk)
+80
View File
@@ -0,0 +1,80 @@
using System;
using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEngine;
using Object = UnityEngine.Object;
namespace OCES
{
public class ResourceLoader : MonoBehaviour
{
readonly Dictionary<string, Object> m_cachedObjects = new();
readonly Dictionary<string, List<Action<Object>>> m_pendingCallbacks = new();
[CanBeNull]
internal T LoadSync<T>(string path) where T : Object
{
if (this.m_cachedObjects.TryGetValue(path, out Object cachedObject))
{
return cachedObject as T;
}
T newObject = Resources.Load<T>(path);
this.m_cachedObjects.Add(path, newObject);
return newObject;
}
internal void LoadAsync<T>(string path, Action<T> onComplete) where T : Object
{
if (this.m_cachedObjects.TryGetValue(path, out Object cachedObject))
{
onComplete?.Invoke(cachedObject as T);
return;
}
if (this.m_pendingCallbacks.TryGetValue(path, out List<Action<Object>> callbacks))
{
callbacks.Add(obj => onComplete?.Invoke(obj as T));
return;
}
this.m_pendingCallbacks[path] = new List<Action<Object>> { obj => onComplete?.Invoke(obj as T) };
ResourceRequest newRequest = Resources.LoadAsync<T>(path);
StartCoroutine(HandleAsyncLoadCompletion(path, newRequest));
}
IEnumerator HandleAsyncLoadCompletion(string path, ResourceRequest request)
{
yield return request;
if (request.asset)
{
this.m_cachedObjects[path] = request.asset;
}
if (this.m_pendingCallbacks.Remove(path, out List<Action<Object>> callbacks))
{
foreach (Action<Object> callback in callbacks)
{
callback?.Invoke(request.asset);
}
}
}
internal void PreloadAsync<T>(string[] paths)
{
}
internal void Release<T>(string path)
{
}
internal void ReleaseUnused()
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 845d98765be843f9a943f41f45cdf013
timeCreated: 1778573329