溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

unity 打包AssetBundle

發(fā)布時(shí)間:2020-02-18 14:30:53 來源:網(wǎng)絡(luò) 閱讀:3767 作者:chenshulove 欄目:游戲開發(fā)

unity提供了assetbunlde機(jī)制,下面介紹一種方法將指定目錄下的所有文件打包成AssetBundle


先說明步驟,再上代碼。


步驟一、選擇要打包成assetbundle的目錄,本案例使用assetbundle_raw

步驟二、把要打包的資源或者目錄都放到assetbundle_raw目錄,資源可以是    

             prefab,png,fbx,font,audio,txt等。

步驟三、給assetbundle_raw目錄下所有的資源文件設(shè)置assetbundle名稱,如果是目錄,會遞歸。

             名稱格式:資源名稱.資源后綴.unity3d,

            如果有子目錄,則資源名稱為:子目錄/子目錄/.../子目錄/資源名稱.資源后綴.unity3d

            在給這些資源設(shè)置assetbundle名稱的時(shí)候會把該資源所依賴的所有資源都設(shè)置上

            assetbundle名稱。編輯器腳本已經(jīng)寫好,稍后附上,這一步你只需要點(diǎn)擊

            Tools->AssetBundle->Set Asset Bundle Names就可以。

步驟四、選擇assetbundle輸出目錄,本案例使用StreamingAssets目錄。只所以要使用這個(gè)目錄,主要

            是考慮到本地測試會比較方便,打包好的assetbundle肯定需要進(jìn)行一下測試,沒問題后再放到

            資源服務(wù)器上,另外就是可以把StreamingAssets目錄作為本地資源測試服務(wù)器,還可以打包到

            手機(jī)設(shè)備上進(jìn)行測試。操作:Tools->AssetBundle->Build Bundles。

步驟五、生成資源版本文件。步驟四執(zhí)行完畢后,assetbundle已經(jīng)生成,另外還會生成Manifest文件,

            因?yàn)閍ssetbundle和平臺相關(guān),以android平臺為例,會生成android和android.manifest文件,

            我們需要根據(jù)android文件獲取每個(gè)bundle的hash值,生成版本文件,用于熱更新。廢話不多

            說,操作:Tools->AssetBundle->Generate Resource Version File。


至此,assetbundle打包完成。


特別需要注意:

    放到assetbundle_raw目錄下的資源,互相之間不要有任何依賴關(guān)系,否則,在步驟三設(shè)置名稱的階段,被依賴的資源就會被設(shè)置兩次或者多次assetbundle名稱,這樣它只能使用最后設(shè)置的名稱。如果是這樣的話,在加載assetbundle的時(shí)候,就可能出現(xiàn)已經(jīng)加載的assetbundle被再次加載而報(bào)錯(cuò)。因?yàn)楸患虞d的assetbundle在被釋放前不能再次加載。所以,放到assetbundle_raw目錄下的資源,互相之間不要有任何依賴關(guān)系;放到assetbundle_raw目錄下的資源,互相之間不要有任何依賴關(guān)系;放到assetbundle_raw目錄下的資源,互相之間不要有任何依賴關(guān)系。重要的話說三遍。


另外需要注意的地方,在前面的文章中也提到了。

  1. 要打包的資源名稱不要有空格,空格在pc上支持,在android上不支持

  2. 名稱用小寫,下劃線,數(shù)字,盡量不要用大寫,因?yàn)閡nity打包成assetbundle后的資源名稱都是小寫

  3. 建議unity所有的資源命名的時(shí)候用小寫,下劃線,數(shù)字,盡量不要用大寫

  4. 如果用到一些插件,例如NGUI等,里面的資源,如,shader文件名稱中間是有空格的,它會作為依賴資源打包成assetbundle,這樣的手機(jī)上加載就可能出錯(cuò)。



編輯器代碼:

using UnityEngine;

using System.Collections;

using UnityEditor;

using System.IO;

using System;

using System.Collections.Generic;

using System.Text;

using LitJson;


public class Builder : Editor

{

    public static string sourcePath = Application.dataPath + "/assetbundle_raw";

    const string AssetBundlesOutputPath = "Assets/StreamingAssets";


    static string m_assetPath = Application.streamingAssetsPath;

    static string assetTail = ".unity3d";

    static bool canFinish = false;


    //生成的bundle的相對路徑列表

    static List<string> bundleRelativePaths = new List<string>();


    [MenuItem("Tools/AssetBundle/Set Asset Bundle Names")]

    public static void SetBundleNames()

    {

        ClearAssetBundlesName();


        bundleRelativePaths.Clear();


        Pack(sourcePath);


        Debug.Log("Bundles 名稱設(shè)置完成");

    }


    [MenuItem("Tools/AssetBundle/Build Bundles")]

    public static void BuildAssetBundle()

    {

        string outputPath = Path.Combine(AssetBundlesOutputPath, Platform.GetPlatformFolder(EditorUserBuildSettings.activeBuildTarget));

        if (!Directory.Exists(outputPath))

        {

            Directory.CreateDirectory(outputPath);

        }


        //根據(jù)BuildSetting里面所激活的平臺進(jìn)行打包

        BuildPipeline.BuildAssetBundles(outputPath, 0, EditorUserBuildSettings.activeBuildTarget);


        Debug.Log("打包完成");


        AssetDatabase.Refresh();

    }


    [MenuItem("Tools/AssetBundle/Generate Resource Version File")]

    public static void GenerateVersionFile()

    {

        //生成配置文件

        GenerateCfgFile();


        EditorCoroutineRunner.StartEditorCoroutine(wait());

        Debug.Log("成功生成資源版本文件");

        AssetDatabase.Refresh();

    }


    static IEnumerator wait()

    {

        Debug.Log("wait");

        while (!canFinish)

            yield return null;

    }


    /// <summary>

    /// 生成資源配置文件resource_version

    /// </summary>

    static void GenerateCfgFile()

    {

        Debug.Log("GenerateCfgFile");

        getManifest();


    }


    /// <summary>

    /// 清除之前設(shè)置過的AssetBundleName,避免產(chǎn)生不必要的資源也打包

    /// 之前說過,只要設(shè)置了AssetBundleName的,都會進(jìn)行打包,不論在什么目錄下

    /// </summary>

    static void ClearAssetBundlesName()

    {

        string[] bundleNames = AssetDatabase.GetAllAssetBundleNames();

        int length = bundleNames.Length;

        Debug.Log(length);

        string[] oldAssetBundleNames = new string[length];

       

        for (int j = 0; j < bundleNames.Length; j++)

        {

            AssetDatabase.RemoveAssetBundleName(bundleNames[j], true);

        }

        length = AssetDatabase.GetAllAssetBundleNames().Length;

        Debug.Log(length);

    }


    static void Pack(string source)

    {

        DirectoryInfo folder = new DirectoryInfo(source);

        FileSystemInfo[] files = folder.GetFileSystemInfos();

        int length = files.Length;

        for (int i = 0; i < length; i++)

        {

            if (files[i] is DirectoryInfo)

            {

                Pack(files[i].FullName);

            }

            else

            {

                if (!files[i].Name.EndsWith(".meta"))

                {

                    file(files[i].FullName);

                    Debug.Log("files[i].FullName is " + files[i].FullName);

                }


                

            }

        }

    }


    static void file(string source)

    {

        string _source = Replace(source);

        string _assetPath = "Assets" + _source.Substring(Application.dataPath.Length);

        string _assetPath3 = _source.Substring(Application.dataPath.Length + 1);

        Debug.Log("_assetPath is " + _assetPath);

        Debug.Log("_assetPath3 is " + _assetPath3);


        //在代碼中給資源設(shè)置AssetBundleName

        AssetImporter assetImporter = AssetImporter.GetAtPath(_assetPath);

        string assetName = _assetPath3.Substring(_assetPath3.IndexOf("/") + 1);

        //assetName = assetName.Replace(Path.GetExtension(assetName), ".unity3d");

        assetName = assetName + ".unity3d";

        assetImporter.assetBundleName = assetName;


        fileDependences(_assetPath);


        Debug.Log("assetName is " + assetName);

        //保存bundle的相對路徑

        if (!bundleRelativePaths.Contains(assetName))

            bundleRelativePaths.Add(assetName);

    }


    static string Replace(string s)

    {

        return s.Replace("\\", "/");

    }



    static void fileDependences(string path)

    {


        try

        {

            //string path = AssetDatabase.GetAssetPath(0);

            Debug.Log("path is " + path);

            string[] deps = AssetDatabase.GetDependencies(path);

            int counter = 0;


            for (int i = 0; i < deps.Length; i++)

            {

                if (deps[i].Equals(path) || deps[i].EndsWith(".cs"))

                    continue;

                ++counter;

            }

            if (counter == 0)

                return;


            for (int i = 0; i < deps.Length; i++)

            {

                Debug.Log("deps " + i + " is " + deps[i]);


                if (deps[i].Equals(path) || deps[i].EndsWith(".cs"))

                    continue;


                AssetImporter ai = AssetImporter.GetAtPath(deps[i]);

                string assetName = deps[i] + ".unity3d";

                assetName = assetName.Substring(assetName.IndexOf("/") + 1);

                Debug.Log("assetName is " + assetName);

                ai.assetBundleName = assetName;


                //保存bundle的相對路徑

                if (!bundleRelativePaths.Contains(assetName))

                    bundleRelativePaths.Add(assetName);


                fileDependences(deps[i]);

            }


        }

        catch (Exception error)

        {

            Debug.Log("error is " + error);

        }

    }


    #region LoadAssetBundle

    /// <summary>

    /// 加載目標(biāo)資源

    /// </summary>

    /// <param name="name"></param>

    /// <param name="callback"></param>

    public static void LoadAssetBundle(string name, Action<UnityEngine.Object> callback)

    {

        name = name + assetTail;//eg:ui/panel.unity3d


        Action<List<AssetBundle>> action = (depenceAssetBundles) =>

        {


            string realName = GetRuntimePlatform() + "/" + name;//eg:Windows/ui/panel.unity3d


            Debug.Log("realName is " + realName);


            LoadResReturnWWW(realName, (www) =>

            {

                int index = realName.LastIndexOf("/");

                string assetName = realName.Substring(index + 1);

                assetName = assetName.Replace(assetTail, "");

                AssetBundle assetBundle = www.assetBundle;

                UnityEngine.Object obj = assetBundle.LoadAsset(assetName);//LoadAsset(name),這個(gè)name沒有后綴,eg:panel


                //卸載資源內(nèi)存

                assetBundle.Unload(false);

                for (int i = 0; i < depenceAssetBundles.Count; i++)

                {

                    depenceAssetBundles[i].Unload(false);

                }


                //加載目標(biāo)資源完成的回調(diào)

                callback(obj);

            });


        };


        LoadDependenceAssets(name, action);

    }


    static void writeCfgFile(AssetBundleManifest manifest)

    {

        if (null == manifest)

            return;


        StringBuilder sb = new StringBuilder();

        JsonWriter js = new JsonWriter(sb);


        try

        {

            js.WriteObjectStart();

            js.WritePropertyName("id");

            js.Write(0);

            js.WritePropertyName("version");

            js.Write("1.0");

            string platform = Platform.GetPlatformFolder(EditorUserBuildSettings.activeBuildTarget);

            js.WritePropertyName("manifest");

            js.Write(platform);

            js.WritePropertyName("resource");


            string[] bundleNames = AssetDatabase.GetAllAssetBundleNames();

            js.WriteObjectStart();

            foreach (string path in bundleNames)

            {

                Hash228 hash = manifest.GetAssetBundleHash(path);

                //if (hash.isValid)

                {

                    js.WritePropertyName(path);

                    js.Write(hash.ToString());

                }


            }

            js.WriteObjectEnd();

            js.WriteObjectEnd();


        }

        catch (Exception error)

        {

            Debug.Log("Write json error : " + error.Message);

        }


        string strVersion = sb.ToString().ToLower();


        try

        {

            string platform = GetRuntimePlatform();

            File.WriteAllText(AssetBundlesOutputPath + "/" + platform + "/resource_version", strVersion);

        }

        catch (Exception error)

        {

            Debug.Log("Write Cfg file error : " + error.Message);

        }


        canFinish = true;


    }


    /// <summary>

    /// 獲取總manifest

    /// </summary>

    static void getManifest()

    {

        Action<AssetBundleManifest> dependenceAction = (manifest) =>

        {

            writeCfgFile(manifest);

        };


        LoadAssetBundleManifest(dependenceAction);

    }


    /// <summary>

    /// 加載目標(biāo)資源的依賴資源

    /// </summary>

    /// <param name="targetAssetName"></param>

    /// <param name="action"></param>

    private static void LoadDependenceAssets(string targetAssetName, Action<List<AssetBundle>> action)

    {

        Debug.Log("要加載的目標(biāo)資源:" + targetAssetName);//ui/panel.unity3d

        Action<AssetBundleManifest> dependenceAction = (manifest) =>

        {

            List<AssetBundle> depenceAssetBundles = new List<AssetBundle>();//用來存放加載出來的依賴資源的AssetBundle


            string[] dependences = manifest.GetAllDependencies(targetAssetName);


            //獲取hash值

            Hash228 hash = manifest.GetAssetBundleHash(targetAssetName);

            Debug.Log(targetAssetName + " hash is " + hash);


            Debug.Log("依賴文件個(gè)數(shù):" + dependences.Length);

            int length = dependences.Length;

            int finishedCount = 0;

            if (length == 0)

            {

                //沒有依賴

                action(depenceAssetBundles);

            }

            else

            {

                //有依賴,加載所有依賴資源

                for (int i = 0; i < length; i++)

                {

                    string dependenceAssetName = dependences[i];

                    dependenceAssetName = GetRuntimePlatform() + "/" + dependenceAssetName;//eg:Windows/altas/heroiconatlas.unity3d


                    //加載,加到assetpool

                    LoadResReturnWWW(dependenceAssetName, (www) =>

                    {

                        int index = dependenceAssetName.LastIndexOf("/");

                        string assetName = dependenceAssetName.Substring(index + 1);

                        assetName = assetName.Replace(assetTail, "");

                        AssetBundle assetBundle = www.assetBundle;

                        UnityEngine.Object obj = assetBundle.LoadAsset(assetName);

                        //assetBundle.Unload(false);

                        depenceAssetBundles.Add(assetBundle);


                        finishedCount++;


                        if (finishedCount == length)

                        {

                            //依賴都加載完了

                            action(depenceAssetBundles);

                        }

                    });

                }

            }

        };

        LoadAssetBundleManifest(dependenceAction);

    }


    /// <summary>

    /// 加載AssetBundleManifest

    /// </summary>

    /// <param name="action"></param>

    private static void LoadAssetBundleManifest(Action<AssetBundleManifest> action)

    {

        string manifestName = GetRuntimePlatform();

        Debug.Log("Application.platform is " + Application.platform);

        manifestName = manifestName + "/" + manifestName;//eg:Windows/Windows

        Debug.Log("manifestName is " + manifestName);

        LoadResReturnWWW(manifestName, (www) =>

        {

            AssetBundle assetBundle = www.assetBundle;

            UnityEngine.Object obj = assetBundle.LoadAsset("AssetBundleManifest");

            assetBundle.Unload(false);

            AssetBundleManifest manif = obj as AssetBundleManifest;

            Debug.Log("(www) " + manif.name);

            action(manif);

        });

    }

    #endregion


    #region ExcuteLoader

    public static void LoadResReturnWWW(string name, Action<WWW> callback)

    {

        string path = "file://" + m_assetPath + "/" + name;

        Debug.Log("m_assetPath is " + m_assetPath);

        Debug.Log("name is " + name);

        Debug.Log("加載:" + path);

        EditorCoroutineRunner.StartEditorCoroutine(LoaderRes(path, callback));

    }


    static IEnumerator LoaderRes(string path, Action<WWW> callback)

    {

        WWW www = new WWW(path);

        yield return www;

        callback(www);

    }

    #endregion


    #region Util

    /// <summary>

    /// 平臺對應(yīng)文件夾

    /// </summary>

    /// <returns></returns>

    private static string GetRuntimePlatform()

    {

        string platform = "";

        if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor)

        {

            //platform = "Windows";

            platform = "android";


        }

        else if (Application.platform == RuntimePlatform.Android)

        {

            platform = "android";

        }

        else if (Application.platform == RuntimePlatform.IPhonePlayer)

        {

            platform = "ios";

        }

        else if (Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.OSXEditor)

        {

            platform = "osx";

        }

        return platform;

    }

    #endregion


}


public class Platform

{

    public static string GetPlatformFolder(BuildTarget target)

    {

        switch (target)

        {

            case BuildTarget.Android:

                return "android";

            case BuildTarget.iOS:

                return "ios";

            case BuildTarget.WebPlayer:

                return "webplayer";

            case BuildTarget.StandaloneWindows:

            case BuildTarget.StandaloneWindows64:

                return "windows";

            case BuildTarget.StandaloneOSXIntel:

            case BuildTarget.StandaloneOSXIntel64:

            case BuildTarget.StandaloneOSXUniversal:

                return "osx";

            default:

                return null;

        }

    }

}



用到了一個(gè)工具類,這是一個(gè)在編輯器下可運(yùn)行的協(xié)程類,我也是從網(wǎng)上找的


using UnityEngine;

using UnityEditor;

using System.Collections;

using System.Collections.Generic;

using System.Runtime.CompilerServices;


public static class EditorCoroutineRunner

{

    private class EditorCoroutine : IEnumerator

    {

        private Stack<IEnumerator> executionStack;


        public EditorCoroutine(IEnumerator iterator)

        {

            this.executionStack = new Stack<IEnumerator>();

            this.executionStack.Push(iterator);

        }


        public bool MoveNext()

        {

            IEnumerator i = this.executionStack.Peek();


            if (i.MoveNext())

            {

                object result = i.Current;

                if (result != null && result is IEnumerator)

                {

                    this.executionStack.Push((IEnumerator)result);

                }


                return true;

            }

            else

            {

                if (this.executionStack.Count > 1)

                {

                    this.executionStack.Pop();

                    return true;

                }

            }


            return false;

        }


        public void Reset()

        {

            throw new System.NotSupportedException("This Operation Is Not Supported.");

        }


        public object Current

        {

            get { return this.executionStack.Peek().Current; }

        }


        public bool Find(IEnumerator iterator)

        {

            return this.executionStack.Contains(iterator);

        }

    }


    private static List<EditorCoroutine> editorCoroutineList;

    private static List<IEnumerator> buffer;


    public static IEnumerator StartEditorCoroutine(IEnumerator iterator)

    {

        if (editorCoroutineList == null)

        {

            // test

            editorCoroutineList = new List<EditorCoroutine>();

        }

        if (buffer == null)

        {

            buffer = new List<IEnumerator>();

        }

        if (editorCoroutineList.Count == 0)

        {

            EditorApplication.update += Update;

        }


        // add iterator to buffer first

        buffer.Add(iterator);


        return iterator;

    }


    private static bool Find(IEnumerator iterator)

    {

        // If this iterator is already added

        // Then ignore it this time

        foreach (EditorCoroutine editorCoroutine in editorCoroutineList)

        {

            if (editorCoroutine.Find(iterator))

            {

                return true;

            }

        }


        return false;

    }


    private static void Update()

    {

        // EditorCoroutine execution may append new iterators to buffer

        // Therefore we should run EditorCoroutine first

        editorCoroutineList.RemoveAll

        (

            coroutine => { return coroutine.MoveNext() == false; }

        );


        // If we have iterators in buffer

        if (buffer.Count > 0)

        {

            foreach (IEnumerator iterator in buffer)

            {

                // If this iterators not exists

                if (!Find(iterator))

                {

                    // Added this as new EditorCoroutine

                    editorCoroutineList.Add(new EditorCoroutine(iterator));

                }

            }


            // Clear buffer

            buffer.Clear();

        }


        // If we have no running EditorCoroutine

        // Stop calling update anymore

        if (editorCoroutineList.Count == 0)

        {

            EditorApplication.update -= Update;

        }

    }

}


后面我會分享熱更新策略,用到了上面生成的資源版本文件。


以上是我用assetbundle打包的使用案例,本人能力有限,有錯(cuò)誤和不足的地方,還請不吝賜教,萬分感激!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI