1. 程式人生 > >Unity5.x新依賴打包及載入

Unity5.x新依賴打包及載入

寫在前面:

很久很久很久。。沒有更新了,當然寫這個需要堅持,最近因為工作調整轉了移動開發,之後的部落格更新會以移動開發為主了,當然寫部落格純粹是為了記錄自己的學習過程,畢竟好記性不如硬鍵盤嘛,有什麼錯誤的地方還望大家指正。

Unity自5.0開始提供了新的AssetBundle打包Api,以下是新舊Api對比:

舊版:
圖1
圖1

打包方式:

public class ExportAssetBundles {
        [MenuItem("Assets/Build AssetBundle From Selection - Track dependencies")]
        static
void ExportResource () { // Bring up save panel string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d"); if (path.Length != 0) { // Build the resource file from the active selection. Object[] selection = Selection.GetFiltered(typeof
(Object), SelectionMode.DeepAssets); BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, 0); Selection.objects = selection; } } [MenuItem("Assets/Build AssetBundle From Selection - No dependency tracking")] static void
ExportResourceNoTrack () { // Bring up save panel string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d"); if (path.Length != 0) { // Build the resource file from the active selection. BuildPipeline.BuildAssetBundle(Selection.activeObject, Selection.objects, path); } } }

而在目前最新的Unity2017版本中,舊版本的打包方式已經被完全廢棄了。

新版打包Api:

圖2
圖2

public class PackageAssetBundle : MonoBehaviour {
    [MenuItem("Build/Build AssetBundles")]
    static void buildAssetBundles()
    {
        string buildPath = Application.dataPath + "/Assets/Abs";//打包路徑
        if (!Directory.Exists(buildPath))
            Directory.CreateDirectory(buildPath);
        BuildPipeline.BuildAssetBundles(buildPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
    }

}

可以看到打包的程式碼非常簡單。

如何指定需要打包的assets?

有幾種方式:
第一種是直接在Inspecot面板進行指定:
圖3
圖3

打包時Api將選擇所有你指定了AssetBundle VarName的資源,然後統一進行打包,如圖3中該資源被打包後最終會生成cube.ab和cube.ab.manifest兩個檔案,manifest檔案中記錄的是cube.ab的資訊,如依賴資源等等。

第二種呢是在程式碼中進行指定:

public class BuildAssetBundlesBuildMapExample : MonoBehaviour
{
    [MenuItem("Example/Build Asset Bundles Using BuildMap")]
    static void BuildMapABs()
    {
        // Create the array of bundle build details.
        AssetBundleBuild[] buildMap = new AssetBundleBuild[2];

        buildMap[0].assetBundleName = "enemybundle";

        string[] enemyAssets = new string[2];
        enemyAssets[0] = "Assets/Textures/char_enemy_alienShip.jpg";
        enemyAssets[1] = "Assets/Textures/char_enemy_alienShip-damaged.jpg";

        buildMap[0].assetNames = enemyAssets;
        buildMap[1].assetBundleName = "herobundle";

        string[] heroAssets = new string[1];
        heroAssets[0] = "char_hero_beanMan";
        buildMap[1].assetNames = heroAssets;

        BuildPipeline.BuildAssetBundles("Assets/ABs", buildMap, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
    }
}

當然指定方式靈活度還是不錯的,你可以直接Selection,然後再對Selection的資源構造成buildMap,再進行處理即可。

打包說完了,接著就是載入咯。

如何載入?

載入可以分為三個步驟:
1.首先讀取Manifest檔案,讀取需要載入ab(AssetBundle)的依賴資源
2.載入依賴ab
3.載入ab

ok,說了這麼多好像。。。

那好吧,接下來還是看個小例子吧.

打包資源如下:

圖4
圖4

依賴關係:

Prefabs depends Materials
cube.ab depend on texure.ab
capsule.ab depend on emission.ab

我們只需要把材質掛在prefabs上即可,Unity新的打包方式會自動幫我們處理依賴關係的,執行打包程式碼後得到如下目錄:
圖5
圖5
show in explorer來看看:
圖6
圖6
如圖6所示,打包完成後,在打包路徑下生成了一個和當前路徑資料夾同名的AssetBundle檔案和一個manifest檔案,這兩個檔案是有用的,別隨便刪除了哈。。。這裡寫圖片描述

但是改個名字什麼的還是可以的哈。

接著來看一下檔案大小,畢竟我們使用依賴打包的目的是為了減小包的大小不是嘛:

ab size
cube.ab 1.72KB
capsule.ab 1.78KB
texure.ab 147KB
emission.ab 510KB

哎,沒錯的吧,cube和capsule中確實剔除了材質,大小隻有一點多KB,來看一下依賴資訊,就是它:
圖7
圖7
我們可以發現裡面清晰的記錄每個ab的依賴資源資訊。

載入方式

AssetBundle的載入方式有好幾種,這裡介紹兩種常用的:

1.LoadFromFile:

public GameObject loadAssetBundleFiles(string filePath, string manifestBundleName, string abFileName, string prefabFileName)
    {
        /**
         * 1.首先從打包路徑獲取依賴
         * 這裡載入的就是生成的同名檔案ab,這裡應該是filePath/Abs,當然會這個名稱也可以修改
         */
        AssetBundle manifestBundle = AssetBundle.LoadFromFile(getManifestFilePath(filePath, manifestBundleName));
        /**
         * 2.獲取依賴資源列表
         */
        if (manifestBundle != null)
        {
            try
            {
                AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");//固定載入方式,通過 assetbundle Abs載入Abs.manifest
                manifestBundle.Unload(false);
                //獲取載入ab的依賴資訊,引數為ab名稱,如cube.ab
                string[] dependsFile = manifest.GetAllDependencies(abFileName);
                if (dependsFile.Length > 0)
                {
                    //根據獲取到的依賴資訊載入所有依賴資源ab
                    AssetBundle[] dependsBundle = new AssetBundle[dependsFile.Length];
                    for (int i = 0; i < dependsFile.Length; i++)
                    {
                        String fp = generateAbsoluteFile(filePath, dependsFile[i]);
                        Debug.Log(String.Format("depends:{0}:{1}", i, dependsFile[i]));
                        dependsBundle[i] = AssetBundle.LoadFromFile(fp);
                    }
                }
            }
            catch (InvalidCastException e)
            {
                Debug.LogException(e);
            }

            /**
             * 3.最後載入ab
             * 注意這裡的LoadAsset的引數是Prefab的名稱,無後綴,如cube而非cube.ab或cube.prefab
             */
            AssetBundle ab = AssetBundle.LoadFromFile(generateAbsoluteFile(filePath, abFileName));
            GameObject go = ab.LoadAsset(prefabFileName) as GameObject;
            ab.Unload(false);
            return go;
        }
        return null;
    }

2.WWW載入:

IEnumerator wwwGetAssetBundles(string filePath, string manifestName, string abFileName, string prefabFileName)
    {
        //WWW www = WWW.LoadFromCacheOrDownload(getManifestFilePath(filePath), 0);
        WWW www = new WWW(manifestName);
        yield return www;
        if (!string.IsNullOrEmpty(www.error))
        {
            Debug.LogError(www.error);
        }
        else
        {
            /**
             * 1.首先從打包路徑獲取依賴
             */
            AssetBundle manifestBundle = www.assetBundle;
            /**
             * 2.獲取依賴資源列表
             */
            if (manifestBundle != null)
            {

                AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
                manifestBundle.Unload(false);
                www.Dispose();
                string[] dependsFile = manifest.GetAllDependencies(abFileName);
                if (dependsFile.Length > 0)
                {
                    AssetBundle[] dependsBundle = new AssetBundle[dependsFile.Length];
                    for (int i = 0; i < dependsFile.Length; i++)
                    {
                        String fp = generateAbsoluteFile(filePath, dependsFile[i]);
                        Debug.Log(String.Format("depends:{0}:{1}", i, dependsFile[i]));
                        www = new WWW(fp);
                        yield return www;
                        if(www.error == null)
                        {
                            dependsBundle[i] = www.assetBundle;
                            www.Dispose();
                        }                        
                    }
                }

                /**
                 * 3.最後載入ab
                 */
                AssetBundle ab = AssetBundle.LoadFromFile(generateAbsoluteFile(filePath, abFileName));
                GameObject go = ab.LoadAsset(prefabFileName) as GameObject;
                if (go != null)
                {
                    GameObject.Instantiate(go);
                }
                ab.Unload(false);
            }
        }
    }

程式碼和第一種載入方式大致相同。

最後說一點使用時需要注意的地方,舉個例子,比如說我們有一公用的資源,A、B、C三個ab都需要依賴D,那麼在程式中我們需要載入A時會先載入D,然後載入A,這個時候B也需要被載入了,因為程式不知道D已經被載入了,所以再次去讀取依賴資訊,嘗試載入D,這樣就會消耗一些cpu的時間了,因此在我們實際開發中應該更加合理的去管理公用的ab資源,對於不常用的公用ab使用過後及時解除安裝,下次使用再進行載入,而對用經常使用需要常駐記憶體的ab資源,最好在程式中設定一個標識,標記其是否已經被載入,存在於記憶體中,然後在載入依賴該公用ab的ab資源時判斷該公用ab是否已被載入,當然,也可能會存在一個ab依賴多個公用資源,這種情況就需要開發者更加謹慎的去處理依賴關係了,最好建立統一的管理,做到邏輯清晰。

Over:最後貼出
官網連結:BuildAssetBundles
Demo下載連結(Unity5.x):Demo地址
密碼:m4q2