Untested code, does not guarantee correctness. The author is my friend ZQY
- Instructions
using System;
using System.Text.RegularExpressions;
using UnityEngine;
public static class AssetBundleHelper
{
/// <summary>
/// 以异步方式从本地文件加载AssetBundle及其依赖的AssetBundle。
/// </summary>
public class LoadAssetBundleWithDependenciesFromFileAsync : CustomYieldInstruction
{
public override bool keepWaiting => !_CheckIsDone();
/// <summary>
/// 操作是否完成。
/// </summary>
/// <returns></returns>
public bool isDone => _CheckIsDone();
/// <summary>
/// 操作进度。
/// </summary>
/// <returns></returns>
public float progress => _GetProgress();
/// <summary>
/// 被加载的目标资源对象。
/// </summary>
public AssetBundle assetBundle => _assetBundle;
/// <summary>
/// 被加载的目标资源对象(索引0)及其依赖的资源对象。
/// </summary>
public AssetBundle[] assetBundles => _assetBundles;
/// <summary>
/// 当操作完成时回调此方法。
/// </summary>
public Action<LoadAssetBundleWithDependenciesFromFileAsync> completed;
private bool _isDone;
private AssetBundle _assetBundle;
private AssetBundle[] _assetBundles;
private AssetBundleCreateRequest[] _requests;
/// <summary>
/// 以异步方式从本地文件加载AssetBundle及其依赖的AssetBundle。
/// </summary>
/// <param name="abRootFolderPath">存放AssetBundle文件的根目录路径</param>
/// <param name="abName">要加载的AssetBundle的名称</param>
/// <param name="manifest">AssetBundleManifest对象</param>
public LoadAssetBundleWithDependenciesFromFileAsync(string abRootFolderPath, string abName, AssetBundleManifest manifest)
{
_StartLoadingAsync(abRootFolderPath, abName, manifest);
}
// 开始异步加载AssetBundle
private void _StartLoadingAsync(string abRootFolderPath, string abName, AssetBundleManifest manifest)
{
_isDone = false;
var dependencies = manifest.GetAllDependencies(abName);
var len = dependencies.Length + 1; // 加上要加载的AB包自身
_assetBundles = new AssetBundle[len];
_requests = new AssetBundleCreateRequest[len];
// 收集异步请求
_requests[0] = AssetBundle.LoadFromFileAsync(abRootFolderPath + abName);
for (int i = 1; i < len; i++)
{
_requests[i] = AssetBundle.LoadFromFileAsync(abRootFolderPath + dependencies[i - 1]);
}
}
// 检查异步加载是否完成
private bool _CheckIsDone()
{
if (_isDone)
{
return _isDone;
}
// 如果有任意一个请求未完成,则认为整体加载未完成
foreach (var req in _requests)
{
if (req.isDone == false)
{
return _isDone;
}
}
_isDone = true;
// 收集加载到的AssetBundle
_assetBundle = _requests[0].assetBundle;
for (int i = 0; i < _requests.Length; i++)
{
_assetBundles[i] = _requests[i].assetBundle;
}
completed?.Invoke(this);
return _isDone;
}
// 获取加载进度
private float _GetProgress()
{
if (_isDone)
{
return 1.0f;
}
var total = 1.0f * _requests.Length;
var current = 0.0f;
foreach (var req in _requests)
{
current += req.progress;
}
return current / total;
}
}
/// <summary>
/// 从本地文件加载AssetBundleManifest对象。
/// </summary>
/// <param name="abRootFolderPath">存放AssetBundle文件的根文件夹路径</param>
/// <param name="rootAbName">根AssetBundle文件的名称(与所在文件夹同名),如果不传递此参数则尝试使用正则表达式主动解析其值</param>
/// <returns></returns>
public static AssetBundleManifest LoadAssetBundleManifest(string abRootFolderPath, string rootAbName = null)
{
if (string.IsNullOrEmpty(rootAbName))
{
rootAbName = GetLastPartNameOfPath(abRootFolderPath);
}
var rootAb = AssetBundle.LoadFromFile(abRootFolderPath + rootAbName);
var manifest = rootAb.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
return manifest;
}
/// <summary>
/// 获取路径中最后一部分的名称(文件名或文件夹名)。
/// </summary>
/// <param name="path">文件路径或文件夹路径</param>
/// <returns></returns>
public static string GetLastPartNameOfPath(string path)
{
// 正则说明:
// [^/\\] :表示匹配除了斜杠(/)和反斜杠(\)以外的任意字符,双反斜杠用于转义
// + :表示匹配前面的表达式一次或多次
// [/\\] :表示匹配斜杠(/)或反斜杠(\)
// * :表示匹配零次或多次
// $ :表示从后向前匹配
// 截取最后一部分名称,名称的末尾可能带有多个斜杠(/)或反斜杠(\)
var pattern = @"[^/\\]+[/\\]*$";
var match = Regex.Match(path, pattern);
var name = match.Value;
// 截取名称中不带斜杠(/)或反斜杠(\)的部分
pattern = @"[^/\\]+";
match = Regex.Match(name, pattern);
name = match.Value;
return name;
}
}