[2019-05-29] Unity coroutine loading custom package AB

Untested code, does not guarantee correctness. The author is my friend ZQY


  • Instructions
12907730-4b235e96238cb235.png

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

Guess you like

Origin blog.csdn.net/weixin_34417183/article/details/90868142