Unity3D关于AssetBundle框架设计(A)

一、思路如下:

①开发专门的标记脚本,自动给指定目录下面的所有合法资源文件(预设、贴图、材质等)添加标记。

②通过写专门的脚本读取Unity自动创建的 *.manifest文件;自动分析和维护AssetBundle包之间的依赖关系,使得包的依赖关系可以实现循环依赖和自动化加载。

③开发针对AssetBundle专门框架,按照一定的严格流程解决AB包加载、复杂依赖、资源提取释放等事宜,尽可能让最终使用的框架人员只关心输入和输出结果部分,屏蔽内部的复杂性。

④开发的AssetBundle框架中,需要对AssetBundle包之间,以及AssetBundle包内存的资源做缓存设计,并且提供参数开关,让开发者自行决定是否应用缓存加载。

二、AssetBundle框架总体分为三个部分

《1》自动化创建AssetBundle

《2》单一AssetBundle包的加载与管理

《3》AssetBundle整体管理

《1》自动化创建AssetBundle(以下三个脚本需要放在Editor文件夹下面)

①自动给资源文件添加标记(AutoSetLableToPrefabs.cs)

/***
 *
 *  Title: "AssetBundle工具包"项目
 *          (自动)给资源文件(预设)添加标记
 *
 *  Description:
 *         开发思路:
 *         1:定位需要打包资源的文件夹根目录。
 *         2:遍历每个“场景”文件夹(目录)
 *            2.1> 遍历本场景目录下的所有的目录或者文件。
                   如果是目录,则继续递归访问里面的文件,直到定位到文件。
 *            2.2> 找到文件,修改AssetBundle 的标签(label)
 *                 具体用AssetImporter 类实现,修改包名与后缀。
 *  Date: 2018
 * 
 *  Version: 1.0
 *
 *  Modify Recorder:
 *     
 */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using UnityEditor;
using System.IO;

namespace ABTools
{

    public class AutoSetLabelToPrefabs
    {
        [MenuItem("AssetBundleTools/Set AB Label")]
        public static void SetABLabels(){
            //需要给AB做标记的根目录
            string strNeedSetABLableRootDIR = string.Empty;
            //目录信息
            DirectoryInfo[] dirScenesDIRArray = null;
            
            //清空无用AB标记
            AssetDatabase.RemoveUnusedAssetBundleNames();
            //定位需要打包资源的文件夹根目录。
            strNeedSetABLableRootDIR = PathTools.GetABResourcesPath();
            DirectoryInfo dirTempInfo = new DirectoryInfo(strNeedSetABLableRootDIR);
            dirScenesDIRArray = dirTempInfo.GetDirectories();
            //遍历每个“场景”文件夹(目录)
            foreach (DirectoryInfo currentDIR in dirScenesDIRArray){
                //遍历本场景目录下的所有的目录或者文件,
                //如果是目录,则继续递归访问里面的文件,直到定位到文件。
                string tmpScenesDIR = strNeedSetABLableRootDIR + "/" + currentDIR.Name;
                DirectoryInfo tmpScenesDIRInfo = new DirectoryInfo(tmpScenesDIR);  //场景目录信息
                int tmpIndex = tmpScenesDIR.LastIndexOf("/");         
                string tmpScenesName = tmpScenesDIR.Substring(tmpIndex+1);         //场景名称

                //递归调用与处理目录或文件系统,如果找到文件,修改AssetBundle 的标签(label)
                JudgeDIROrFileByRecursive(currentDIR,tmpScenesName);
            }//foreach_end

            //刷新
            AssetDatabase.Refresh();
            //提示
            Debug.Log("AssetBundles 本次操作设置成功!");
        }

        /// <summary>
        /// 递归调用与处理目录或文件系统
        /// 1:如果是目录,则进行递归调用。
        /// 2:如果是文件,则给文件做“AB标记”
        /// </summary>
        /// <param name="dirInfo">目录信息</param>
        /// <param name="scenesName">场景名称</param>
        private static void JudgeDIROrFileByRecursive(FileSystemInfo fileSysInfo,string scenesName){
            if (!fileSysInfo.Exists) {
                Debug.LogError("文件或目录名称: " + fileSysInfo.Name + " 不存在,请检查!");
                return;
            }

            //得到当前目录下一级的文件信息集合
            DirectoryInfo dirInfoObj = fileSysInfo as DirectoryInfo;
            FileSystemInfo[] fileSysArray = dirInfoObj.GetFileSystemInfos();
            foreach (FileSystemInfo fileInfo in fileSysArray) {
                FileInfo fileInfoObj = fileInfo as FileInfo;
                //文件类型
                if (fileInfoObj!=null){
                    //修改此文件的AssetBundle的标签
                    SetFileABLabel(fileInfoObj, scenesName);
                }
                //目录类型
                else {
                    //递归下一层
                    JudgeDIROrFileByRecursive(fileInfo, scenesName);
                }
            }
        }

        /// <summary>
        /// 修改文件的AssetBundle 标记
        /// </summary>
        /// <param name="fileInfo">文件信息</param>
        /// <param name="scenesName">场景名称</param>
        private static void SetFileABLabel(FileInfo fileInfo,string scenesName){
            //AssetBundle 包名称
            string strABName = string.Empty;
            //(资源)文件路径(相对路径)
            string strAssetFilePath = string.Empty;

            //参数检查
            if (fileInfo.Extension == ".meta") return;
            //得到AB包名
            strABName = GetABName(fileInfo, scenesName).ToLower();
            /* 使用AssetImporter 类,修改名称与后缀 */
            //获取资源文件相对路径
            int tmpIndex = fileInfo.FullName.IndexOf("Assets");
            strAssetFilePath = fileInfo.FullName.Substring(tmpIndex);
            //给资源文件设置AB名称与后缀
            AssetImporter tmpAssetImportObj = AssetImporter.GetAtPath(strAssetFilePath);
            tmpAssetImportObj.assetBundleName = strABName;  //设置AB包名
            if (fileInfo.Extension==".unity")               //设置AB包扩展名称 
                tmpAssetImportObj.assetBundleVariant = "u3d";
            else
                tmpAssetImportObj.assetBundleVariant = "ab";//AB资源包
        }

        /// <summary>
        /// 获取包名
        /// </summary>
        /// <param name="fileInfo">文件信息</param>
        /// <param name="scenesName">场景名称</param>
        /// <returns>
        /// 返回: 包名称
        /// </returns>
        private static string GetABName(FileInfo fileInfo,string scenesName)
        {
            string strABName = string.Empty;

            //Win路径
            string tmpWinPath = fileInfo.FullName;
            //Unity路径
            string tmpUnityPath = tmpWinPath.Replace("\\","/");
            //定位“场景名称”后面的字符位置
            int tmpSceneNamePosIndex = tmpUnityPath.IndexOf(scenesName) + scenesName.Length;
            //AB文件名称大体区域
            string strABFileNameArea = tmpUnityPath.Substring(tmpSceneNamePosIndex + 1);

            if (strABFileNameArea.Contains("/"))
            {
                string[] tmpStrArray=strABFileNameArea.Split('/');
                strABName = scenesName + "/" + tmpStrArray[0];
            }
            else {
                strABName = scenesName+"/"+ scenesName;
            }

            return strABName;
        }

    }//Class_end
}

②打包资源且输出路径(BuildAssetBundle.cs)

/***
 *
 *  Title: "AssetBundle工具包"项目
 *         给AssetBundle目录(资源)打包
 *
 *  Description:
 *        功能:[本脚本的主要功能描述] 
 *
 *  Date: 2018
 * 
 *  Version: 1.0
 *
 *  Modify Recorder:
 *     
 */

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

namespace ABTools
{
    public class BuildAssetBundle
    {
        /// <summary>
        /// 打包生成所有AssetBundles
        /// </summary>
        [MenuItem("AssetBundleTools/BuildAllAssetBundles")]
        public static void BuildAllAB()
        {
            //(打包)AB的输出路径
            string strABOutPathDIR = string.Empty;

            strABOutPathDIR = PathTools.GetABOutPath();
            if (!Directory.Exists(strABOutPathDIR))
            {
                Directory.CreateDirectory(strABOutPathDIR);
            }
            //打包生成
            BuildPipeline.BuildAssetBundles(strABOutPathDIR,BuildAssetBundleOptions.None,BuildTarget.StandaloneWindows64);
        }

    }//Class_end
}

③删除路径中的所有资源(DeleteAssetBundle.cs)

/***
 *
 *  Title: "AssetBundle工具包"项目
 *         删除AssetBundle包文件
 *
 *  Description:
 *        功能:[本脚本的主要功能描述] 
 *
 *  Date: 2018
 * 
 *  Version: 1.0
 *
 *  Modify Recorder:
 *     
 */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;

namespace ABTools
{
    public class DeleteAssetBundle 
    {
        [MenuItem("AssetBundleTools/DeleteAllAssetBundles")]
        public static void DeleteAllABs()
        {
            //(打包)AB的输出路径
            string strNeedDeleteDIR = string.Empty;

            strNeedDeleteDIR = PathTools.GetABOutPath();
            if (!string.IsNullOrEmpty(strNeedDeleteDIR))
            {
                //参数true 表示可以删除非空目录。
                Directory.Delete(strNeedDeleteDIR, true);
                //去除删除警告
                File.Delete(strNeedDeleteDIR + ".meta");
                //刷新
                AssetDatabase.Refresh();
            }
        }
    }//Class_end
}

注意:涉及到路径及其常量定义专门用一个Helps文件夹存储

/***
 *
 *  Title: "AssetBundle工具包"项目
 *         项目常量、枚举、委托等定义
 *
 *  Description:
 *         注意:
 *             不包含关于本项目路径信息,由其他帮助类单独管理
 *
 *  Date: 2018
 * 
 *  Version: 1.0
 *
 *  Modify Recorder:
 *     
 */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


namespace ABTools
{
    /* 委托定义区 */
    /// <summary>
    /// AssetBundle 加载完成
    /// </summary>
    /// <param name="abName">AssetBundle 名称</param>
    public delegate void DelLoadComplete(string abName);

    /* 枚举类型定义区 */

    /*  本项目常量定义区 */
    public class ABDefine 
    {
        public static string ASSETBUNDLE_MANIFEST = "AssetBundleManifest";
    }//Class_end
}
/***
 *
 *  Title: "AssetBundle工具包"项目
 *         路径工具类
 *
 *  Description:
 *        功能:包含路径方法、路径常量
 *
 *  Date: 2018
 * 
 *  Version: 1.0
 *
 *  Modify Recorder:
 *     
 */

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;


namespace ABTools
{
    public class PathTools 
    {
        /* 路径常量 */
        public const string AB_RESOURCES = "AB_Res";//AB_Resources
        
        /// <summary>
        /// 获取AB资源(输入目录)
        /// </summary>
        /// <returns></returns>
        public static string GetABResourcesPath()
        {
            return Application.dataPath + "/"+ AB_RESOURCES;
        }

        /// <summary>
        /// 获取AB输出路径
        /// 说明:
        ///     由两部分构成
        ///     1: 平台(PC/移动端)路径。
        ///     2: 平台(PC/移动端)名称
        /// </summary>
        /// <returns></returns>
        public static string GetABOutPath()
        {
            return GetPlatformPath() + "/" + GetPlatformName();
        }

        /// <summary>
        /// 获取WWW协议路径
        /// </summary>
        /// <returns></returns>
        public static string GetWWWPath()
        {
            string strReturnWWWPath = string.Empty;

            switch (Application.platform)
            {
                case RuntimePlatform.WindowsPlayer:
                case RuntimePlatform.WindowsEditor:
                    strReturnWWWPath = "file://"+GetABOutPath();
                    break;
                case RuntimePlatform.Android:
                    strReturnWWWPath = "jar:file://" + GetABOutPath();
                    break;
                default:
                    break;
            }

            return strReturnWWWPath;
        }

        /// <summary>
        /// 获取平台路径
        /// </summary>
        /// <returns></returns>
        private static string GetPlatformPath()
        {
            string strReturnPlatformPath = string.Empty;

            switch (Application.platform)
            {
                case RuntimePlatform.WindowsPlayer:
                case RuntimePlatform.WindowsEditor:
                    strReturnPlatformPath = Application.streamingAssetsPath;
                    break;
                case RuntimePlatform.IPhonePlayer:
                case RuntimePlatform.Android:
                    strReturnPlatformPath = Application.persistentDataPath;
                    break;
                default:
                    break;
            }

            return strReturnPlatformPath;
        }

        /// <summary>
        /// 获取平台名称
        /// </summary>
        /// <returns></returns>
        public static string GetPlatformName()
        {
            string strReturnPlatformName = string.Empty;

            switch (Application.platform)
            {
                case RuntimePlatform.WindowsPlayer:
                case RuntimePlatform.WindowsEditor:
                    strReturnPlatformName = "Win";
                    break;
                case RuntimePlatform.IPhonePlayer:
                    strReturnPlatformName = "Iphone";
                    break;
                case RuntimePlatform.Android:
                    strReturnPlatformName = "Android";
                    break;
                default:
                    break;
            }

            return strReturnPlatformName;
        } 

    }//Class_end
}

《2》单一AssetBundle包的加载与管理

/***
 *
 *  Title: "AssetBundle工具包"项目
 *         第1层: AB资源加载类
 *
 *  Description:
 *        功能:管理指定AssetBundle 中的资源
 *              1:加载AssetBundle
 *              2: 卸载、释放AssetBundle 资源
 *              3:查看当前AssetBundle内资源
 *
 *  Date: 2018
 * 
 *  Version: 1.0
 *
 *  Modify Recorder:
 *     
 */

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


namespace ABTools
{
    public class AssetLoader : System.IDisposable{
        //当前AssetBundle
        private AssetBundle _CurrentAssetBundle;
        //容器键值对集合
        private Hashtable _Ht;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="abObj">给定AssetBundle</param>
        public AssetLoader(AssetBundle abObj){
            if (abObj!=null){
                _CurrentAssetBundle = abObj;
                _Ht = new Hashtable();
            }
            else
                Debug.LogError(GetType()+ "/构造函数AssetLoader()/参数abObj==null!,请检查!");
        }

        /// <summary>
        /// 加载当前包中指定单个资源
        /// 说明:带有资源缓冲功能
        /// </summary>
        /// <param name="assetName"></param>
        /// <returns></returns>
        public UnityEngine.Object LoadAsset(string assetName,bool isCache=false){
            return LoadResource<UnityEngine.Object>(assetName, isCache);
        }

        /// <summary>
        /// 加载当前包资源,带缓冲技术
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assetName"></param>
        /// <param name="isCache"></param>
        /// <returns></returns>
        private T LoadResource<T>(string assetName, bool isCache) where T:UnityEngine.Object{
            if (_Ht.Contains(assetName)){
                return _Ht[assetName] as T;
            }

            T tmpTResource = _CurrentAssetBundle.LoadAsset<T>(assetName);
            if (tmpTResource!=null && isCache)
            {
                _Ht.Add(assetName, tmpTResource);
            }
            else if(tmpTResource==null)
            {
                Debug.LogError(GetType() + "/LoadResource<T>()/参数tmpTResource==null!,请检查!");
            }

            return tmpTResource;
        }
    

        /// <summary>
        /// 卸载资源
        /// 功能: 卸载指定AssetBundle 包中指定资源
        /// </summary>
        /// <param name="asset"></param>
        /// <returns>
        /// true: 表明卸载资源成功
        /// </returns>
        public bool UnLoadAsset(UnityEngine.Object asset)
        {
            if (asset!=null)
            {
                Resources.UnloadAsset(asset);
                return true;
            }
            Debug.LogError(GetType() + "/UnLoadAsset()/参数asset==null!,请检查!");
            return false;
        }

        /// <summary>
        /// 释放当前AssetBundle资源(包)
        /// </summary>
        public void Dispose()
        {
            _CurrentAssetBundle.Unload(false);
        }

        /// <summary>
        /// 释放当前AssetBundle资源(包),且卸载所有资源
        /// </summary>
        public void DisposeALL()
        {
            _CurrentAssetBundle.Unload(true);
        }

        /// <summary>
        /// 查询当前AsserBundle 所有资源
        /// </summary>
        /// <returns></returns>
        public string[] RetrivalALLAssetName()
        {
            return _CurrentAssetBundle.GetAllAssetNames();
        }
    }//Class_end
}
/***
 *
 *  Title: "AssetBundle工具包"项目
 *         第2层:WWW加载AssetBundle
 *
 *  Description:
 *        功能:
 *
 *  Date: 2018
 * 
 *  Version: 1.0
 *
 *  Modify Recorder:
 *     
 */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


namespace ABTools
{
    public class SingleABLoader:System.IDisposable
    {
        //引用类: 资源加载类
        private AssetLoader _AssetLoader;
        //委托: 加载完成
        private DelLoadComplete _LoadCompleteHandle;
        //AssetBundle名称
        private string _ABName;
        //AssetBundle 下载路径
        private string _ABDownloadPath;

   
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="abName"></param>
        /// <param name="loadProgress"></param>
        /// <param name="loadComplete"></param>
        public SingleABLoader(string abName, DelLoadComplete loadComplete)
        {
            _ABName = abName;
            _LoadCompleteHandle = loadComplete;
            _ABDownloadPath = PathTools.GetWWWPath() + "/" + _ABName;
            _AssetLoader = null;
        }

        /// <summary>
        /// 加载AssetBundle资源包
        /// </summary>
        /// <returns></returns>
        public IEnumerator LoadAssetBundle(){
            using (WWW www = new WWW(_ABDownloadPath)){
                yield return www;
                if (www.progress >= 1){
                    //加载完成,获取AssetBundel实例
                    AssetBundle abObj = www.assetBundle;
                    if (abObj != null){
                        _AssetLoader = new AssetLoader(www.assetBundle);
                        if (_LoadCompleteHandle != null)
                            _LoadCompleteHandle(_ABName);
                    }
                    else{
                        Debug.LogError(GetType() + "/LoadAssetBundle()/WWW 下载出错,请检查 AssetBundle URL :" + _ABDownloadPath + " 错误信息: " + www.error);
                    }
                }
            }//using_end
        }//Method_end

        /// <summary>
        /// 加载(AB包内)资源
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="isCache">是否使用缓存</param>
        /// <returns></returns>
        public UnityEngine.Object LoadAsset(string assetName,bool isCache)
        {
            if (_AssetLoader != null)
            {
                return _AssetLoader.LoadAsset(assetName, isCache);
            }
            Debug.LogError(GetType() + "/LoadAsset()/参数 _AssetLoader ==null!,请检查!");
            return null;
        }

        /// <summary>
        /// 卸载(AB包内)资源
        /// </summary>
        /// <param name="asset">资源名称</param>
        public void UnLoadAsset(UnityEngine.Object asset)
        {
            if (_AssetLoader != null)
            {
                _AssetLoader.UnLoadAsset(asset);
            }
            else {
                Debug.LogError(GetType() + "/UnLoadAsset()/参数 _AssetLoader ==null!,请检查!");
            }
        }

        /// <summary>
        /// 释放(AB包)
        /// </summary>
        public void Dispose()
        {
            if (_AssetLoader != null)
            {
                _AssetLoader.Dispose();
                _AssetLoader = null;
            }
            else {
                Debug.LogError(GetType() + "/Dispose()/参数 _AssetLoader ==null!,请检查!");
            }
        }

        /// <summary>
        /// 释放当前AssetBundle资源(包),且卸载所有资源
        /// </summary>
        public void DisposeALL()
        {
            if (_AssetLoader != null)
            {
                _AssetLoader.DisposeALL();
                _AssetLoader = null;
            }
            else
            {
                Debug.LogError(GetType() + "/DisposeALL()/参数 _AssetLoader ==null!,请检查!");
            }
        }

        /// <summary>
        /// 查询当前AssetBundle包内所有资源
        /// </summary>
        /// <returns></returns>
        public string[] RetrivalALLAssetName()
        {
            if (_AssetLoader != null)
            {
                return _AssetLoader.RetrivalALLAssetName();
            }
            Debug.LogError(GetType() + "/RetrivalALLAssetName()/参数 _AssetLoader ==null!,请检查!");
            return null;
        }


    }//Class_end
}

三、进行测试这个核心的框架内容 

/***
 *
 *  Title: "AssetBundle工具包"项目
 *         "AssetBundle框架"内部阶段验证测试
 *        
 *
 *  Description:
 *         检测“SingleABLoader”类工作是否正常。
 *
 *  Date: 20178
 * 
 *  Version: 1.0
 *
 *  Modify Recorder:
 *     
 */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


namespace ABTools
{
    public class SingleABLoader_TestClass:MonoBehaviour {
        //引用类
        SingleABLoader loaderObj = null;
        //AB包名称
        private string _ABName1 = "commonscenes/prefabs_1.ab";
        //AB包内资源名称
        private string _AssetName = "Cylinder";


        private void Start(){
            loaderObj = new SingleABLoader(_ABName1, LoadCompleate);
            //加载AB资源包
            StartCoroutine(loaderObj.LoadAssetBundle());
        }

        private void LoadCompleate(string abName){
            Debug.Log("abName= " + abName + " 调用完毕!");
            //加载资源
            UnityEngine.Object tmpCloneObj= loaderObj.LoadAsset(_AssetName,false);
            Instantiate(tmpCloneObj);

            //显示AB包内资源
            string[] strArray = loaderObj.RetrivalALLAssetName();
            Debug.Log("包内所有资源");
            foreach (string item in strArray)
            {
                Debug.Log(item);
            }
        }

        private void Update()
        {
            //释放AB包
            if (Input.GetKeyDown(KeyCode.A))
            {
                print("释放AB包");
                loaderObj.Dispose();
            }
        }

    }//Class_end
}

 注意:本内容来自《Unity3D/2D游戏开发从0到1》第30章

猜你喜欢

转载自blog.csdn.net/xiaochenXIHUA/article/details/83684164