313 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			313 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C#
		
	
	
| 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<ResLoaderBase> OnLoaderFree;
 | |
| 
 | |
|         private Queue<OneResLoadTask> _taskPool = new Queue<OneResLoadTask>(10);
 | |
|         
 | |
| 
 | |
|         #region 加载逻辑
 | |
| 
 | |
|         internal static OneResLoadTask CreateBundleTask(string url, Action<ResLoadInfo> callback, Action<bool, OneResLoadTask> handler)
 | |
|         {
 | |
|             return OneResLoadTask.Create(url, callback, handler, UnityWebRequestAssetBundle.GetAssetBundle(url));
 | |
|         }
 | |
| 
 | |
|         internal static OneResLoadTask CreateTextureTask(string url, Action<ResLoadInfo> callback, Action<bool, OneResLoadTask> handler)
 | |
|         {
 | |
|             return OneResLoadTask.Create(url, callback, handler, UnityWebRequestTexture.GetTexture(url, true));;
 | |
|         }
 | |
|         
 | |
|         internal static OneResLoadTask CreateFileTask(string url, Action<ResLoadInfo> callback, Action<bool, OneResLoadTask> handler)
 | |
|         {
 | |
|             return OneResLoadTask.Create(url, callback, handler, new UnityWebRequest(url));;
 | |
|         }
 | |
|         
 | |
|         
 | |
|         /// <summary>
 | |
|         /// 异步加载
 | |
|         /// </summary>
 | |
|         /// <param name="resUri"></param>
 | |
|         /// <param name="onLoadComplete"></param>
 | |
|         public void LoadAsync(string resUri, Action<ResLoadInfo> 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;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         /// <summary>
 | |
|         /// 开始异步加载
 | |
|         /// </summary>
 | |
|         /// <param name="task"></param>
 | |
|         /// <param name="onLoadComplete"></param>
 | |
|         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 资源
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 加载 Bundle
 | |
|         /// </summary>
 | |
|         /// <param name="url"></param>
 | |
|         /// <param name="onLoadComplete"></param>
 | |
|         public void LoadBundle(string url, Action<ResLoadInfo> 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<ResLoadInfo> 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<ResLoadInfo> 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
 | |
|         
 | |
|         
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// 单挑加载任务类
 | |
|     /// </summary>
 | |
|     internal struct OneResLoadTask
 | |
|     {
 | |
|         public string url;
 | |
|         public int retryTimes;
 | |
|         public Action<ResLoadInfo> callback;
 | |
|         public UnityWebRequest www;
 | |
|         public Action<bool, OneResLoadTask> loadHandler;
 | |
|         
 | |
|         
 | |
|         public static OneResLoadTask Create(string url, Action<ResLoadInfo> callback, Action<bool, OneResLoadTask> handler,
 | |
|             UnityWebRequest www)
 | |
|         {
 | |
|             return new OneResLoadTask()
 | |
|             {
 | |
|                 url = url,
 | |
|                 callback = callback,
 | |
|                 loadHandler = handler,
 | |
|                 www = www,
 | |
|                 retryTimes = 0,
 | |
|             };
 | |
|         }
 | |
|     }
 | |
| 
 | |
| } |