using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; using Object = UnityEngine.Object; namespace Guru { public enum ResType { Unknown = 0, Bundle, Texture, File, } public class ResUriHelper { public const string FilePrefix = "file::"; public const string BundlePrefix = "bundle::"; public const string TexturePrefix = "texture::"; public const string HttpPrefix = "http://"; public const string HttpsPrefix = "https://"; public static string GetFileUri(string url) { return $"{FilePrefix}{url}"; } public static string GetBundleUri(string url) { return $"{BundlePrefix}{url}"; } public static string GetTextureUri(string url) { return $"{TexturePrefix}{url}"; } public static void UriToType(string uri, out ResType type, out string url) { type = ResType.Unknown; url = uri; if (uri.StartsWith(FilePrefix)) { type = ResType.File; url = uri.Substring(FilePrefix.Length); } else if (uri.StartsWith(BundlePrefix)) { type = ResType.Bundle; url = uri.Substring(BundlePrefix.Length); } else if (uri.StartsWith(TexturePrefix)) { type = ResType.Texture; url = uri.Substring(TexturePrefix.Length); } else if (uri.StartsWith(HttpPrefix) || uri.StartsWith(HttpsPrefix)) { type = ResType.File; url = uri; } } } public class ResLoaderBase: IResLoader { public int RetryTimes { get; set; } = 3; private bool _iSBusy = false; public bool IsBusy { get => _iSBusy; set { _iSBusy = value; if(!_iSBusy) OnLoaderFree?.Invoke(this); } } public Action OnLoaderFree; private Queue _taskPool = new Queue(10); #region 加载逻辑 internal static OneResLoadTask CreateBundleTask(string url, Action callback, Action handler) { return OneResLoadTask.Create(url, callback, handler, UnityWebRequestAssetBundle.GetAssetBundle(url)); } internal static OneResLoadTask CreateTextureTask(string url, Action callback, Action handler) { return OneResLoadTask.Create(url, callback, handler, UnityWebRequestTexture.GetTexture(url, true));; } internal static OneResLoadTask CreateFileTask(string url, Action callback, Action handler) { return OneResLoadTask.Create(url, callback, handler, new UnityWebRequest(url));; } /// /// 异步加载 /// /// /// public void LoadAsync(string resUri, Action onLoadComplete) { ResUriHelper.UriToType(resUri, out var type, out var url); switch (type) { case ResType.Bundle: LoadBundle(url, onLoadComplete); break; case ResType.Texture: LoadTexture(url, onLoadComplete); break; case ResType.File: LoadFile(url, onLoadComplete); break; default: Debug.LogError($"--- Unknown ResType: {type}"); break; } } /// /// 开始异步加载 /// /// /// private void StartAsyncLoading(OneResLoadTask task) { IsBusy = true; task.www.SendWebRequest().completed += ao => { if (task.www.result == UnityWebRequest.Result.Success) { task.loadHandler?.Invoke(true, task); } else { if (task.retryTimes < RetryTimes) { task.retryTimes++; StartAsyncLoading(task); } else { task.loadHandler?.Invoke(false, task); } } }; } private void AddLoadTask(OneResLoadTask task) { if (IsBusy) { _taskPool.Enqueue(task); // 入队等待 } else { StartAsyncLoading(task); // 直接加载 } } #endregion #region 加载 Bundle 资源 /// /// 加载 Bundle /// /// /// public void LoadBundle(string url, Action onLoadComplete) { AddLoadTask(CreateBundleTask(url, onLoadComplete, OnBundleLoaded)); } private void OnBundleLoaded(bool success, OneResLoadTask task) { var info = new ResLoadInfo(task.url); if (success) { var ab = DownloadHandlerAssetBundle.GetContent(task.www); if(ab != null) { info.success = true; info.target = ab; info.bundleName = ab.name; } } task.callback?.Invoke(info); SetFree(); } #endregion #region 加载二进制文件 public void LoadFile(string url, Action onLoadComplete) { if (IsBusy) { Debug.LogError($"--- Loader is busy, canot load this task"); return; } StartAsyncLoading(CreateFileTask(url, onLoadComplete, OnFileLoaded)); } private void OnFileLoaded(bool success, OneResLoadTask task) { var info = new ResLoadInfo(task.url); if (success) { var bytes = task.www.downloadHandler?.data ?? null; if(bytes != null) { info.success = true; info.target = bytes; } } task.callback?.Invoke(info); SetFree(); } #endregion #region 加载图片 public void LoadTexture(string url, Action onLoadComplete) { var request = CreateTextureTask(url, onLoadComplete, OnTextureLoaded); if (!IsBusy) { StartAsyncLoading(request); } _taskPool.Enqueue(request); } void OnTextureLoaded(bool success, OneResLoadTask task) { var info = new ResLoadInfo(task.url); if (success) { var texture = DownloadHandlerTexture.GetContent(task.www); if (texture != null) { info.success = true; info.target = texture; } } task.callback?.Invoke(info); SetFree(); } #endregion #region 加载器空闲 private void SetFree() { if (_taskPool.Count > 0) { StartAsyncLoading(_taskPool.Dequeue()); } IsBusy = false; } #endregion } /// /// 单挑加载任务类 /// internal struct OneResLoadTask { public string url; public int retryTimes; public Action callback; public UnityWebRequest www; public Action loadHandler; public static OneResLoadTask Create(string url, Action callback, Action handler, UnityWebRequest www) { return new OneResLoadTask() { url = url, callback = callback, loadHandler = handler, www = www, retryTimes = 0, }; } } }