1. 程式人生 > >Unity3D關於AssetBundle框架設計(A)

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章