319 lines
10 KiB
C#
319 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using UnityEngine;
|
|
using YooAsset;
|
|
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
namespace TH1Resource
|
|
{
|
|
public static class ResourceLoader
|
|
{
|
|
public const string BundleResourceRoot = "Assets/BundleResources";
|
|
|
|
private static readonly Dictionary<string, AssetHandle> Handles = new Dictionary<string, AssetHandle>();
|
|
private static readonly string[] DefaultExtensions =
|
|
{
|
|
".prefab",
|
|
".asset",
|
|
".png",
|
|
".jpg",
|
|
".jpeg",
|
|
".bytes",
|
|
".txt",
|
|
".json",
|
|
".csv",
|
|
".mat",
|
|
".anim",
|
|
".controller",
|
|
".wav",
|
|
".WAV",
|
|
".mp3",
|
|
".MP3",
|
|
".ogg",
|
|
".OGG",
|
|
".spriteatlas",
|
|
".renderTexture",
|
|
".shader",
|
|
".compute"
|
|
};
|
|
|
|
public static T Load<T>(string location) where T : UnityEngine.Object
|
|
{
|
|
var normalizedLocation = NormalizeLocation(location);
|
|
if (string.IsNullOrEmpty(normalizedLocation))
|
|
{
|
|
Debug.LogError($"[TH1.YooAsset] Empty resource location for {typeof(T).Name}.");
|
|
return null;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
return LoadFromAssetDatabase<T>(normalizedLocation);
|
|
}
|
|
#endif
|
|
|
|
if (!ResourceManager.IsInitialized)
|
|
{
|
|
Debug.LogError($"[TH1.YooAsset] Resource package is not initialized. location={normalizedLocation}, type={typeof(T).Name}");
|
|
return null;
|
|
}
|
|
|
|
if (!TryResolveRuntimeLocation<T>(normalizedLocation, out var resolvedLocation))
|
|
{
|
|
Debug.LogError($"[TH1.YooAsset] Missing resource location: {normalizedLocation}, type={typeof(T).Name}");
|
|
return null;
|
|
}
|
|
|
|
var key = $"{typeof(T).FullName}:{resolvedLocation}";
|
|
if (Handles.TryGetValue(key, out var cachedHandle))
|
|
{
|
|
var cachedAsset = cachedHandle.GetAssetObject<T>();
|
|
if (cachedAsset != null) return cachedAsset;
|
|
|
|
cachedHandle.Release();
|
|
Handles.Remove(key);
|
|
}
|
|
|
|
try
|
|
{
|
|
var handle = YooAssets.LoadAssetSync<T>(resolvedLocation);
|
|
var asset = handle.GetAssetObject<T>();
|
|
if (asset == null)
|
|
{
|
|
handle.Release();
|
|
Debug.LogError($"[TH1.YooAsset] Loaded null resource: {resolvedLocation}, type={typeof(T).Name}");
|
|
return null;
|
|
}
|
|
|
|
Handles[key] = handle;
|
|
return asset;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"[TH1.YooAsset] Load failed: {resolvedLocation}, type={typeof(T).Name}\n{e}");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static void ReleaseAll()
|
|
{
|
|
foreach (var handle in Handles.Values)
|
|
{
|
|
handle.Release();
|
|
}
|
|
|
|
Handles.Clear();
|
|
}
|
|
|
|
private static string NormalizeLocation(string location)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(location)) return string.Empty;
|
|
|
|
var normalized = location.Replace('\\', '/').Trim();
|
|
if (normalized.StartsWith("Assets/BundleResources/", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
normalized = normalized.Substring("Assets/BundleResources/".Length);
|
|
}
|
|
else if (normalized.StartsWith("Assets/Resources/", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
normalized = normalized.Substring("Assets/Resources/".Length);
|
|
}
|
|
|
|
return normalized.TrimStart('/');
|
|
}
|
|
|
|
private static bool TryResolveRuntimeLocation<T>(string location, out string resolvedLocation) where T : UnityEngine.Object
|
|
{
|
|
foreach (var candidate in EnumerateRuntimeLocationCandidates<T>(location))
|
|
{
|
|
if (YooAssets.CheckLocationValid(candidate))
|
|
{
|
|
resolvedLocation = candidate;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
resolvedLocation = location;
|
|
return false;
|
|
}
|
|
|
|
private static IEnumerable<string> EnumerateRuntimeLocationCandidates<T>(string location) where T : UnityEngine.Object
|
|
{
|
|
var emitted = new HashSet<string>(StringComparer.Ordinal);
|
|
foreach (var candidate in EnumerateLocationAndAssetPath(location))
|
|
{
|
|
if (emitted.Add(candidate))
|
|
{
|
|
yield return candidate;
|
|
}
|
|
}
|
|
|
|
if (Path.HasExtension(location)) yield break;
|
|
|
|
foreach (var extensionLocation in EnumerateLocationCandidates<T>(location))
|
|
{
|
|
foreach (var candidate in EnumerateLocationAndAssetPath(extensionLocation))
|
|
{
|
|
if (emitted.Add(candidate))
|
|
{
|
|
yield return candidate;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<string> EnumerateLocationCandidates<T>(string location) where T : UnityEngine.Object
|
|
{
|
|
var emitted = new HashSet<string>(StringComparer.Ordinal);
|
|
foreach (var extension in GetPreferredExtensions<T>())
|
|
{
|
|
if (emitted.Add(extension))
|
|
{
|
|
yield return location + extension;
|
|
}
|
|
}
|
|
|
|
foreach (var extension in DefaultExtensions)
|
|
{
|
|
if (emitted.Add(extension))
|
|
{
|
|
yield return location + extension;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<string> EnumerateLocationAndAssetPath(string location)
|
|
{
|
|
yield return location;
|
|
|
|
if (!location.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
yield return $"{BundleResourceRoot}/{location}";
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<string> GetPreferredExtensions<T>() where T : UnityEngine.Object
|
|
{
|
|
var type = typeof(T);
|
|
if (type == typeof(GameObject))
|
|
{
|
|
yield return ".prefab";
|
|
}
|
|
else if (type == typeof(Sprite) || type == typeof(Texture) || type == typeof(Texture2D))
|
|
{
|
|
yield return ".png";
|
|
yield return ".jpg";
|
|
yield return ".jpeg";
|
|
}
|
|
else if (type == typeof(TextAsset))
|
|
{
|
|
yield return ".bytes";
|
|
yield return ".txt";
|
|
yield return ".json";
|
|
yield return ".csv";
|
|
}
|
|
else if (type == typeof(AudioClip))
|
|
{
|
|
yield return ".wav";
|
|
yield return ".WAV";
|
|
yield return ".mp3";
|
|
yield return ".MP3";
|
|
yield return ".ogg";
|
|
yield return ".OGG";
|
|
}
|
|
else if (type == typeof(AnimationClip))
|
|
{
|
|
yield return ".anim";
|
|
}
|
|
else if (type == typeof(Material))
|
|
{
|
|
yield return ".mat";
|
|
}
|
|
else if (type == typeof(RuntimeAnimatorController))
|
|
{
|
|
yield return ".controller";
|
|
}
|
|
else if (type == typeof(Shader))
|
|
{
|
|
yield return ".shader";
|
|
}
|
|
else if (typeof(ScriptableObject).IsAssignableFrom(type))
|
|
{
|
|
yield return ".asset";
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private static T LoadFromAssetDatabase<T>(string location) where T : UnityEngine.Object
|
|
{
|
|
if (TryResolveEditorAssetPath<T>(location, out var resolvedAssetPath))
|
|
{
|
|
return AssetDatabase.LoadAssetAtPath<T>(resolvedAssetPath);
|
|
}
|
|
|
|
var targetPath = $"{BundleResourceRoot}/{location}".Replace('\\', '/');
|
|
var directory = Path.GetDirectoryName(targetPath)?.Replace('\\', '/');
|
|
var fileName = Path.GetFileName(targetPath);
|
|
|
|
if (string.IsNullOrEmpty(directory) || !AssetDatabase.IsValidFolder(directory))
|
|
{
|
|
Debug.LogError($"[TH1.YooAsset] Editor resource folder missing: {directory}, location={location}");
|
|
return null;
|
|
}
|
|
|
|
var guids = AssetDatabase.FindAssets(fileName, new[] { directory });
|
|
foreach (var guid in guids)
|
|
{
|
|
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
|
|
if (RemoveExtension(assetPath).Equals(RemoveExtension(targetPath), StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var asset = AssetDatabase.LoadAssetAtPath<T>(assetPath);
|
|
if (asset != null) return asset;
|
|
}
|
|
}
|
|
|
|
Debug.LogError($"[TH1.YooAsset] Editor resource missing: {location}, type={typeof(T).Name}");
|
|
return null;
|
|
}
|
|
|
|
private static bool TryResolveEditorAssetPath<T>(string location, out string assetPath) where T : UnityEngine.Object
|
|
{
|
|
var targetPath = $"{BundleResourceRoot}/{location}".Replace('\\', '/');
|
|
if (Path.HasExtension(targetPath) && File.Exists(targetPath))
|
|
{
|
|
assetPath = targetPath;
|
|
return true;
|
|
}
|
|
|
|
if (!Path.HasExtension(targetPath))
|
|
{
|
|
foreach (var candidate in EnumerateLocationCandidates<T>(targetPath))
|
|
{
|
|
if (File.Exists(candidate))
|
|
{
|
|
assetPath = candidate.Replace('\\', '/');
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
assetPath = targetPath;
|
|
return false;
|
|
}
|
|
|
|
private static string RemoveExtension(string assetPath)
|
|
{
|
|
var extension = Path.GetExtension(assetPath);
|
|
return string.IsNullOrEmpty(extension)
|
|
? assetPath.Replace('\\', '/')
|
|
: assetPath.Substring(0, assetPath.Length - extension.Length).Replace('\\', '/');
|
|
}
|
|
#endif
|
|
}
|
|
}
|