Unity3D游戏开发程序员Unity技术分享

Unity5.x AssetBundle 的变化

2017-03-15  本文已影响189人  小飞不会飞_

前言:下面文字适用于对AssetBundle有一点了解的朋友,阅读大约10分钟,AssetBundle基本概念等知识可以网上找一下。

1、打包API的变化

Unity5.x中,AssetBundle相关的API做了极大的简化,合并了多个情况的函数,合并了资源和场景打包函数。


BuildPipeline API.png

1、资源

public static bool BuildAssetBundle(UnityEngine.Object mainAsset, UnityEngine.Object[] assets, string pathName, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform);

mainAssets:指定mainAsset,这样解析该AB包的时候可以通过assetBundle.mainAsset得到
assets:指定打在一起的Asset,解析时可通过LoadAsset(“name”)得到
pathName:打包生成的存储路径
assetBundleOptions:打包选项
BuildTarget:打包目标平台

BuildAssetBundleExplicitAssetNames增加对AssetBundle命名,在4.x中,资源打包后的名字就是主资源的名字。

简单说就是,只要给你要打包的资源设置一个AssetBundleName,在打包的时候Unity就是自动对这些设置了名字的资源进行打包,名字相同的打成一个bundle,并且自动处理资源间的依赖关系,最后生成每个AssetBundle文件和对应一个AssetBundleManifest文件,AssetBundleManifest中记录的就是该bundle的依赖等信息。

使用BuildPipeline.BuildAssetBundles对游戏资源打包:API地址

public static AssetBundleManifest BuildAssetBundles(string outputPath, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform);
public static AssetBundleManifest BuildAssetBundles(string outputPath, AssetBundleBuild[] builds, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform);

outputPath : 生成到目录
AssetBundleBuild:对要打包的资源的封装的数组

该结构有三个属性,assetBundleName(设置AssetBundle的名字)、assetBundleVariant(可以理解为二级名字)、assetNames(数组)

Variant参数:
在Inspector界面最下方,除了可以设置AssetBundle的名字,后面还可以指定Variant参数。打包时,Variant会作为后缀添加在Bundle名字之后,相同的AssetBundle Name,不同的Variant Name,能够让AssetBundle方便地进行“多分辨率支持”。


Variant.png

BuildAssetBundleOptions : 打包选项(后面详细说)
BuildTarget : 打包的目标平台,Android or iOS ,and many

参数基本和unity4.x类似,只是资源的表示方式上不同。

2、场景

public static string BuildStreamedSceneAssetBundle(string[] levels, string locationPath, BuildTarget target, BuildOptions options);

3、选项

BuildAssetBundleOptions:AssetBundle的打包策略,可以根据需求设置最合适的打包策略。

Unity5.x新增

LZMA(Ziv-Markov chain algorithm)格式
Unity打包成AssetBundle时的默认格式,会将序列化数据压缩成LZMA流,使用时需要整体解包。优点是打包后体积小,缺点是解包时间长,且占用内存。
LZ4格式
5.3新版本添加的压缩格式,压缩率不及LZMA,但是不需要整体解压。LZ4是基于chunk的算法,加载对象时只有响应的chunk会被解压。

2、打包过程

1、资源

unity4.x:下面代码是将某个文件中的材质打包,GetDependencies可以将直接和间接的依赖都找到。

public void BuildABundle()
    {
        string[] assets = AssetDatabase.FindAssets("t:mat", new string[] { "Assets/Material" });
        string[] deps = AssetDatabase.GetDependencies(assets);
        for (int i = 0; i < deps.Length; ++i)
        {
            Object ast = AssetDatabase.LoadAssetAtPath(deps[i], typeof(Object));
            BuildPipeline.BuildAssetBundle(ast, new Object[] { }, "Assets/AssetBundles/Material"
                , BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets
                , BuildTarget.StandaloneWindows64);
        }
    }

unity5.x:

public void BuildABundle()
    {
        BuildPipeline.BuildAssetBundles(Application.dataPath + "/AssetBundles"
            , BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
    }
public void BuildABundle()
    {
        List<AssetBundleBuild> wrap = new List<AssetBundleBuild>();
        string[] assets = AssetDatabase.FindAssets("t:mat", new string[] { "Assets/Material" });
        string[] deps = AssetDatabase.GetDependencies(assets);
        string path = "";
        AssetBundleBuild build;
        for (int i = 0; i < deps.Length; ++i)
        {
            path = deps[i];
            build = new AssetBundleBuild();
            build.assetBundleName = this.GetFileName(path) + "_mat.assetBundle";
            //build.assetBundleVariant
            build.assetNames = new string[] { path };
            wrap.Add(build);
        }
        BuildPipeline.BuildAssetBundles("", wrap.ToArray(), BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
    }

    public string GetFileName(string inputPath)
    {
        int index = inputPath.LastIndexOf('\\');
        if (index < 0)
        {
            index = inputPath.LastIndexOf('/');
        }
        int start = index + 1;
        start = (start < 0 ? 0 : start);
        int end = inputPath.LastIndexOf('.');
        end = (end < 0 ? inputPath.Length : end);
        return inputPath.Substring(start, end - start);
    }

需要注意:Unity会将AssetBundle名字设置为相同的资源打包在一个bundle里面。

2、场景

unity4.x:

string[] assets = AssetDatabase.FindAssets("t:unity", new string[] { "Assets/Scene" });
        BuildPipeline.BuildStreamedSceneAssetBundle(assets, "Assets/AssetBundles/Scene", BuildTarget.StandaloneWindows64);

unity5.x:和打包普通资源一样。

以上的代码只是作为例子,实际打包时考虑的东西有很多。

3、依赖打包

unity4.x:不是一般的麻烦,需要自己去建立依赖关系,并且自己维护依赖数据。提供了两个API,分别是BuildPipeline.PushAssetDependencies和BuildPipeline.PopAssetDependencies。现在已经很少用,所以简单写一下

public void BuildABundle()
    {
        string[] assets = AssetDatabase.FindAssets("t:unity", new string[] { "Assets/Scene" });
        Object ast = null;
        string[] deps = null;
        for (int i = 0; i < assets.Length; ++i)
        {
            deps = AssetDatabase.GetDependencies(new string[] { assets[i] });
            BuildPipeline.PushAssetDependencies();
            for (int j = 0; j < deps.Length; ++j)
            {
                if (assets[i] != deps[j])
                {
                    ast = AssetDatabase.LoadAssetAtPath(deps[j], typeof(Object));
                    BuildPipeline.BuildAssetBundle(ast, new Object[] { }, "Assets/AssetBundles/Scene"
                        , BuildAssetBundleOptions.None //打包策略需要仔细考虑,这里随便写的一个
                        , BuildTarget.StandaloneWindows64);
                }
            }
            BuildPipeline.PopAssetDependencies();

            ast = AssetDatabase.LoadAssetAtPath(assets[i], typeof(Object));
            BuildPipeline.BuildAssetBundle(ast, new Object[] { }, "Assets/AssetBundles/Scene"
                , BuildAssetBundleOptions.None //打包策略需要仔细考虑,这里随便写的一个
                , BuildTarget.StandaloneWindows64);
        }
    }

例子只是演示怎么用,真正写的时候会有一个递归的过程。

一个Push对应一个Pop,打包当前资源的时候,会把当前栈中的依赖做为依赖添加到资源bundle中。还得保证在打包某个资源之前,它的依赖已经打包好了,不然就会出现丢各种东西。

unity5.x:现在根本不用自己依赖打包,因为unity已经自动做了,所以代码和之前的一样。

4、依赖管理

unity4.x:自己把依赖关系记录下来,以便在加载的时候可以知道先加载的依赖,这样才能正确的加载资源,具体可查看这篇博客

unity5.x:在打Bundle的时候,同时会为每个bundle生成一个配置文件(.manifest文件
),里面记录着bundle的信息

ManifestFileVersion: 0
CRC: 2829116721
Hashes:
  AssetFileHash:
    serializedVersion: 2
    Hash: 2e559c427b01f4b1438782d05c4d59f2
  TypeTreeHash:
    serializedVersion: 2
    Hash: 87623bb9f607f4edb72c2338fde167fc
HashAppended: 0
ClassTypes:
- Class: 1
  Script: {instanceID: 0}
- Class: 4
  Script: {instanceID: 0}
- Class: 21
  Script: {instanceID: 0}
- Class: 23
  Script: {instanceID: 0}
- Class: 33
  Script: {instanceID: 0}
- Class: 43
  Script: {instanceID: 0}
- Class: 65
  Script: {instanceID: 0}
Assets:
- Assets/Cube.prefab
Dependencies:
- F:/Test/Assets/AssetBundles/mat.sd

版本号
CRC
Asset File的Hash Code,全局唯一ID
Type Tree的Hash Code,全局唯一ID,AssetBundle所包含的所有类型(目前不了解)
Class types:AssetBundle包含的所有"类型"(可以参看unity序列化
Asset names:包含的资源
Dependent AssetBundle names:资源的所有依赖

在unity doc中详细的讲了里面有哪些信息,详情请点击这里

目前只是在做增量打包的时候会用到,但打包过程中完全不用管理依赖和manifest文件,因为无论是依赖还是增量,unity都已经做好了,在加载资源的时候只要调用统一的接口可以取到依赖,并且已经按照“正确的顺序”了,只需循环数组加载。

5、AssetBundle加载

unity4.x和unity5.x的资源加载方式并没有多大变化,API上没有变化,只是在依赖加载的时候,unity5.x实在太方便,既不需要自己做依赖关系,也不需要递归去加载。

先回顾一下API:

string netpath = "http://www.ab.com/down/test.assetBundle";//可以从网络上下载
    string path = Application.persistentDataPath + "/ab";//也可以是本地路径
    int versionCode = 1; //版本号
    private IEnumerator _LoadAB()
    {
        WWW www = new WWW(path);
        yield return www;
        AssetBundle ab = www.assetBundle;
        Object a = ab.LoadAsset("asset");
        GameObject.Instantiate(a);
        www.Dispose();
        www = null;
        ab.Unload(false);
    }

    private IEnumerator _LoadABorCache()
    {
        //先检查本地是否有缓存资源,没有再从网络获取,并且获取后进行缓存。
        WWW www = WWW.LoadFromCacheOrDownload(netpath, versionCode);
        yield return www;
        AssetBundle ab = www.assetBundle;
        Object a = ab.LoadAsset("asset");
        GameObject.Instantiate(a);
        www.Dispose();//释放掉WWW资源
        www = null;
        ab.Unload(false);
    }

unity5.x :
AssetBundle.LoadFromFile
AssetBundle.LoadFromFileAsync
AssetBundle.LoadFromMemory
AssetBundle.LoadFromMemoryAsync

需要注意的是,unity5.x中,LoadFromFile和LoadFromFileAsync已经支持直接加载压缩文件了。并且新机制打包无法指定Assetbundle.mainAsset,因此无法再通过mainAsset来直接获取资源
开启DisableWriteTypeTree可能造成AssetBundle对Unity版本的兼容问题,但会使Bundle更小,同时也会略微提高加载速度。

void Start () {
        AssetBundle manifestAb = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundles");
        AssetBundleManifest manifest = manifestAb.LoadAsset("AssetBundleManifest") as AssetBundleManifest;
        string[] deps = manifest.GetAllDependencies("Cube");
        List<AssetBundle> depList = new List<AssetBundle>();
        for (int i = 0; i < deps.Length; ++i)
        {
            AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + deps[i]);
            depList.Add(ab);
        }

        AssetBundle cubeAb = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/Cube");
        Object org = cubeAb.LoadAsset("Cube");
        Instantiate(org);

        cubeAb.Unload(false);
        for (int i = 0; i < depList.Count; ++i)
        {
            depList[i].Unload(false);
        }
        manifestAb.Unload(true);
    }

unity4.x : 会把文件整个生成内存镜像
AssetBundle加载以后,在内存中只是一块内存数据,没有特定的结构,内存包含了webstream、压缩数据(如果是压缩资源就包含)、解压buffer、解压后的数据。需要使用www.Dispose释放掉webstream的内存。

unity5.3+:最新的加载方式,只会载入header,在真正加载Asset的时候,通过这些header到文件中去取相应的数据。详情

5、资源加载

AssetBundle加载到内存中以后,还需要调用一些Load函数,把Asset加载出来,第一次Load的时候,会比较慢,unity会根据Asset的结构到原始数据中去找对应的内存,取到数据并且生成相应的结构,这样内存中就又多了一些Asset结构。

unity4.x :
AssetBundle.Load
AssetBundle.LoadAsync
AssetBundle.LoadAll

unity5.x :
AssetBundle.LoadAsset
AssetBundle.LoadAssetAsync
AssetBundle.LoadAllAssets
AssetBundle.LoadAllAssetsAsync

5、资源卸载

AssetBundle.Unload(flase)是释放AssetBundle文件的内存镜像,不包含Load创建的Asset内存对象。
AssetBundle.Unload(true)是释放那个AssetBundle文件内存镜像和并销毁所有通过该bundle创建的Asset内存对象。

有些没有引用的“游离”的资源,没有API直接卸载它的内存镜像,只有调用Resources.UnloadUnusedAssets,这个函数很耗时,它会遍历所有内存中的资源,找出并释放掉没有使用的资源。还可以用Resources.UnloadAsset(Object assetToUnload),卸载单个确定的“资源”,注意不是GameObject,因为很麻烦找某个GameObject所用的资源(Texture、Mesh、Anim、Audio),所以一般不会用它。

6、AssetBundle内存结构

AssetBundle内存结构.png
上一篇下一篇

猜你喜欢

热点阅读