com.guru.unity.applit/Runtime/Framework/Extention/ResLoaderBase.cs

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,
};
}
}
}