温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

unity 打包AssetBundle

发布时间:2020-02-18 14:30:53 来源:网络 阅读:3768 作者:chenshulove 栏目:游戏开发

unity提供了assetbunlde机制,下面介绍一种方法将指定目录下的所有文件打包成AssetBundle


先说明步骤,再上代码。


步骤一、选择要打包成assetbundle的目录,本案例使用assetbundle_raw

步骤二、把要打包的资源或者目录都放到assetbundle_raw目录,资源可以是    

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

步骤三、给assetbundle_raw目录下所有的资源文件设置assetbundle名称,如果是目录,会递归。

             名称格式:资源名称.资源后缀.unity3d,

            如果有子目录,则资源名称为:子目录/子目录/.../子目录/资源名称.资源后缀.unity3d

            在给这些资源设置assetbundle名称的时候会把该资源所依赖的所有资源都设置上

            assetbundle名称。编辑器脚本已经写好,稍后附上,这一步你只需要点击

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

步骤四、选择assetbundle输出目录,本案例使用StreamingAssets目录。只所以要使用这个目录,主要

            是考虑到本地测试会比较方便,打包好的assetbundle肯定需要进行一下测试,没问题后再放到

            资源服务器上,另外就是可以把StreamingAssets目录作为本地资源测试服务器,还可以打包到

            手机设备上进行测试。操作:Tools->AssetBundle->Build Bundles。

步骤五、生成资源版本文件。步骤四执行完毕后,assetbundle已经生成,另外还会生成Manifest文件,

            因为assetbundle和平台相关,以android平台为例,会生成android和android.manifest文件,

            我们需要根据android文件获取每个bundle的hash值,生成版本文件,用于热更新。废话不多

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


至此,assetbundle打包完成。


特别需要注意:

    放到assetbundle_raw目录下的资源,互相之间不要有任何依赖关系,否则,在步骤三设置名称的阶段,被依赖的资源就会被设置两次或者多次assetbundle名称,这样它只能使用最后设置的名称。如果是这样的话,在加载assetbundle的时候,就可能出现已经加载的assetbundle被再次加载而报错。因为被加载的assetbundle在被释放前不能再次加载。所以,放到assetbundle_raw目录下的资源,互相之间不要有任何依赖关系;放到assetbundle_raw目录下的资源,互相之间不要有任何依赖关系;放到assetbundle_raw目录下的资源,互相之间不要有任何依赖关系。重要的话说三遍。


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

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

  2. 名称用小写,下划线,数字,尽量不要用大写,因为unity打包成assetbundle后的资源名称都是小写

  3. 建议unity所有的资源命名的时候用小写,下划线,数字,尽量不要用大写

  4. 如果用到一些插件,例如NGUI等,里面的资源,如,shader文件名称中间是有空格的,它会作为依赖资源打包成assetbundle,这样的手机上加载就可能出错。



编辑器代码:

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 名称设置完成");

    }


    [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);

        }


        //根据BuildSetting里面所激活的平台进行打包

        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>

    /// 清除之前设置过的AssetBundleName,避免产生不必要的资源也打包

    /// 之前说过,只要设置了AssetBundleName的,都会进行打包,不论在什么目录下

    /// </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);


        //在代码中给资源设置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>

    /// 加载目标资源

    /// </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),这个name没有后缀,eg:panel


                //卸载资源内存

                assetBundle.Unload(false);

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

                {

                    depenceAssetBundles[i].Unload(false);

                }


                //加载目标资源完成的回调

                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>

    /// 加载目标资源的依赖资源

    /// </summary>

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

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

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

    {

        Debug.Log("要加载的目标资源:" + 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("依赖文件个数:" + 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>

    /// 平台对应文件夹

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

        }

    }

}



用到了一个工具类,这是一个在编辑器下可运行的协程类,我也是从网上找的


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打包的使用案例,本人能力有限,有错误和不足的地方,还请不吝赐教,万分感激!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI