feat: Unified ResourceLoader
- 新增 统一 `ResourceLoader`类,负责同步/异步加载资源,并对已加载的资源缓存。 - 修改 `MusicTransition`, `AudioSystem`, `LongAudioContainerPlayer`, `MusicSegment`, 'SfxSystem`, `HapticSystem`使用最新的`ResourceLoader`。
This commit is contained in:
@@ -18,6 +18,8 @@ namespace OCES.Audio
|
||||
public IReadOnlyDictionary<Type, Enum> ActiveStates { get; private set; }
|
||||
public enum ConsoleLogLevel { Off, Log, Warning, Error } //TODO 实现这个功能
|
||||
|
||||
internal ResourceLoader ResourceLoader;
|
||||
|
||||
const string k_audioConfigPath = "AudioData";
|
||||
const string k_audioResourcePath = "Audios";
|
||||
|
||||
@@ -29,6 +31,7 @@ namespace OCES.Audio
|
||||
AudioMixer m_mixer;
|
||||
Tween m_lowpassTween;
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// 公开接口
|
||||
// ─────────────────────────────────────────────
|
||||
@@ -280,7 +283,9 @@ namespace OCES.Audio
|
||||
Instance = this;
|
||||
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 调度器 ──
|
||||
// AudioSystem.cs 中
|
||||
@@ -409,9 +414,13 @@ namespace OCES.Audio
|
||||
|
||||
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)
|
||||
{
|
||||
Debug.LogError($"未找到表 {tableName}");
|
||||
return default;
|
||||
}
|
||||
|
||||
IBinarySerializable data = new T();
|
||||
bool readOk = FileManager.ReadBinaryDataFromBytes(bytes.bytes, ref data);
|
||||
if (readOk)
|
||||
|
||||
@@ -340,7 +340,12 @@ namespace OCES.Audio
|
||||
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)
|
||||
{
|
||||
Debug.LogError($"[LongAudioContainerPlayer] 音频文件未找到: {segment.Name}");
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace OCES.Audio
|
||||
{
|
||||
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)
|
||||
{
|
||||
Debug.LogError($"[MusicSegmentConfig] 音频文件未找到: {segment.Name}, SegmentId: {segment.Id}");
|
||||
|
||||
@@ -405,7 +405,12 @@ namespace OCES.Audio
|
||||
bool SetupSource(AudioSource source, ActiveSound activeSound, int clipIndex = 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
Debug.LogError($"音频文件未找到:{audioObject.Name[clipIndex]}");
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace OCES.Haptic
|
||||
[NonSerialized]
|
||||
public bool IsMeetsAdvanceRequirements;
|
||||
|
||||
internal ResourceLoader ResourceLoader;
|
||||
|
||||
HapticObjectConfig m_hapticObjects;
|
||||
const string k_hapticConfigPath = "HapticData/";
|
||||
const string k_hapticResourcesPath = "Haptics/";
|
||||
@@ -111,6 +113,8 @@ namespace OCES.Haptic
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
this.ResourceLoader = gameObject.AddComponent<ResourceLoader>();
|
||||
|
||||
this.m_hapticObjects = HapticConfigLoader.Load<HapticObjectConfig>(k_hapticConfigPath + "HapticObject");
|
||||
this.IsHapticSupported = DeviceCapabilities.isVersionSupported;
|
||||
this.IsMeetsAdvanceRequirements = DeviceCapabilities.meetsAdvancedRequirements;
|
||||
@@ -118,16 +122,19 @@ namespace OCES.Haptic
|
||||
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
Debug.LogError($"未找到表 {tableName}");
|
||||
return default;
|
||||
}
|
||||
IBinarySerializable data = new T();
|
||||
bool readOk = FileManager.ReadBinaryDataFromBytes(bytes.bytes, ref data);
|
||||
if (readOk)
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user