Unity 热更新之AssetBundle

来源:互联网 发布:手机淘宝投诉电话多少 编辑:程序博客网 时间:2024/05/18 02:38

前言:

  这几天刚好看到了Unity内存管理部分,所以写下来让以后自己可以复习下,同时也是分享给大家,看看大家觉得哪里不足,再进行更完善。 说起AssetBundle,大家可能都不陌生吧,它的最大的好处就是热更新!用的到热更新的地方一般是大型的网络游戏,因为他的资源大小可能有2G(大概的数字)以上,这时候AssetBundle就起到很大作用!其他地方能不用它尽量不要用,因为这个用起来很麻烦且有些地方还有局限!比如:

 . 资源之间的依赖关系上解决起来颇为让人抓狂,下面会给出讲解。

 . 如果场景中肯定有很多公用的模型,打包场景的话那么内存与size就是公用模型的size * N个场景,在这里有个解决的办法:

(1).首先把场景中公用的部分和私有的部分统统放入Unity;

(2)然后烘培整个场景。 当场景烘培完毕后把公用的模型部分在拿出去,场景只只保留私有的模型。还可以做一个工具将公用模型在场景中的坐标保存在Json/XML/二进制文件中(每个场景文件会对应一个公用模型的Json信息),再将公用的模型分别封装在别的Assetbundle中;

(3)最后服务器上提供每个场景的Assetbundle ,和公用模型的Assetbundle,一般公用模型的Assetbundle可以放在常驻内存中(可能使用频繁、根据项目的不同而定)场景Assetbundle下载完毕后,现载入场景然后在根据场景对应的XML信息将公用模型部分动态的在添加到场景中,这样就完成了一个场景的构建。

       这里会给出网上有现成的例子,主要讲的是如何把场景里的物体记录Json/XML/二进制文件中去,和去解析文件信息! 链接:http://www.xuanyusong.com/archives/1901

http://www.xuanyusong.com/archives/1919

. 在移动平台下Assetbundle里面放的脚本是不会被执行的,在手机上将Assetbundle下载到本地后,加载进游戏中Prefab会自动在本地找它身上挂着的脚本,他是根据脚本的名来寻找,如果本地有这条脚本的话,Prefab会把这个脚本重新绑定在自身,并且会把打包前的参数传递进来。如果本地没有,身上挂的条脚本永远都不会被执行。所以动态加载就有一定的局限性。

进入主题:


    在这里我会通过简单的小实例,进行资源打包和本地和远程服务器进行动态加载AssetBundle这两个方面来讲解,最后会讲解下上面的资源的依赖和版本上要注意一些的细节!

     首选你得会把资源打包.unity3d或者.assetbundle,可能大家会问,这两种文件有什么区别?其实没什么区别,编码格式是相同的,也就是说这两种后缀名其实对应的是同一种文件,因为在打包的的时候,后缀名是自己自定义的!

一般来说assetbundle是按不同平台设置来导出资源的,而.unity3d则只在PC平台导出使用。现在就从这两方面说下怎打包AssetBundle.


资源打包:

    这里的资源打包,你可以在运行时手动单个打包,或者批量打包又或者几个打包在一起,也可以在编辑下进行同样的操作,为了简单展示功能,笔者在这进行的是运行时打包,编辑下打包请见连接地址http://www.xuanyusong.com/archives/2405/       如有疑惑者,请在下方留言。


1.打包成.assetbundle:

 

<span style="font-family:Microsoft YaHei;font-size:14px;color:#660000;">Caching.CleanCache();  string SavePath = Application.dataPath + "/StreamingAssets/" ; Object sharedAsset = AssetDatabase.LoadMainAssetAtPath("Assets/models/textures/texture_Orc_Cleaver.png");        BuildPipeline.BuildAssetBundle(sharedAsset, null, SavePath + sharedAsset.name + ".assetbundle", buildOp, BuildTarget.StandaloneWindows);    AssetDatabase.Refresh();</span>

2.打包成.unity3d:

 Caching.CleanCache();// 在本地测试的情况下,你可能会频繁的打包生成Assetbundle,如果忘记改版本号的话可能会读取之前的缓存,可能就会看不到新的效果,所以我建议在bunild Assetbundle的时候强制清空一下缓存。

<span style="font-family:Microsoft YaHei;font-size:14px;color:#660000;"> string[] path = { "Assets/Test.unity" };        BuildPipeline.BuildPlayer(path, "Test.unity3d", BuildTarget.StandaloneWindows, BuildOptions.BuildAdditionalStreamedScenes);    AssetDatabase.Refresh();</span>



资源下载:


从服务器加载AssetBundle(.assetbundle):

 

<span style="font-family:Microsoft YaHei;font-size:14px;color:#333333;">private IEnumerator LoadMainCacheGameObject(string path) {             WWW www = WWW.LoadFromCacheOrDownload(path, 1);        yield return www;        if (!System.String.IsNullOrEmpty(www.error))        {            Debug.Log(www.error);        }        else        {            Object main = www.assetBundle.mainAsset;            GameObject.Instantiate(main);        }  }</span>


      这里只要你把path要是远程服务器所下载的文件地址,比如,我分别试过用jsp和php搭建了一个服务器,然后把要下载的文件放到起目录下,然后把这个文件的地址传进来就行!

从本地加载AssetBundle(.assetbundle):

       同样这里只要你把path改为本地文件的地址传进来就行!

  注意:

在这里要注意一点的就是 WWW www = WWW.LoadFromCacheOrDownload(path, Version)的第二个参数Version版本号。这里说说从服务器下载时吧,当版本号为1文件已经缓存到本地,此时将Version改成2 那么它又会重新去服务器下载,如果还保持版本号为1那么它会从本地的缓存读取。

解决资源间的依赖关系:

      当你有多个模型用到同一个材质或者一张纹理时,材质还好,资源大小比较小,可是如果事贴图的话,那重复打包就可能占很多的内存,这时候你要单独给纹理打包成一个AssetBUndle,否者它将会被打包多次,其简单做法如下:

       

<span style="font-family:Microsoft YaHei;font-size:14px;color:#660000;"> Caching.CleanCache();                string SavePath =  Application.dataPath + "/StreamingAssets/;        BuildPipeline.PushAssetDependencies();        // 共享资源texture_Orc_Cleaver.png         Object sharedAsset = AssetDatabase.LoadMainAssetAtPath("Assets/Resources/Textures/texture_Orc_Cleaver.png");        BuildPipeline.BuildAssetBundle(sharedAsset, null, SavePath + sharedAsset.name + ".assetbundle", buildOp, BuildTarget.StandaloneWindows);             // Prefab1,引用了texture_Orc_Cleaver.png         BuildPipeline.PushAssetDependencies();        Object p1Asset = AssetDatabase.LoadMainAssetAtPath("Assets/Resources/Prefabs/P1.prefab");        BuildPipeline.BuildAssetBundle(p1Asset, null, SavePath + p1Asset.name + ".assetbundle",  BuildAssetBundleOptions.CollectDependencies, BuildTarget.StandaloneWindows);        BuildPipeline.PopAssetDependencies();        // Prefab2,引用了texture_Orc_Cleaver.png         BuildPipeline.PushAssetDependencies();        Object p2Asset = AssetDatabase.LoadMainAssetAtPath("Assets/Resources/PreFabs/P2.prefab");        BuildPipeline.BuildAssetBundle(p2Asset, null, SavePath + p2Asset.name + ".assetbundle",  BuildAssetBundleOptions.CollectDependencies,       BuildTarget.StandaloneWindows);        BuildPipeline.PopAssetDependencies();        BuildPipeline.PopAssetDependencies();        EditorUtility.DisplayDialog("", "打包完成!", "OK");        AssetDatabase.Refresh();</span>


     

<span style="font-family:Microsoft YaHei;"><span style="font-size:18px;color: rgb(51, 0, 51);">    </span><span style="font-size:14px;color:#660000;">OnGUI()      {        public static readonly string PathURL = "file://" + Application.dataPath + "/StreamingAssets/";        if (GUI.Button(new Rect(0f, 30f, 100f, 20f), "Load Share Res"))        {            StartCoroutine(Load(PathURL +"texture_Orc_Cleaver.assetbundle", 1));        }        if (GUI.Button(new Rect(0f, 60f, 100f, 20f), "Load And Instantiate Prefab"))        {            StartCoroutine(LoadAndInstantiate(PathURL +"P1.assetbundle", 1));            StartCoroutine(LoadAndInstantiate(PathURL +"P2.assetbundle", 1));        }    }    // 加载    IEnumerator Load(string url, int version)    {        WWW www = WWW.LoadFromCacheOrDownload(url, version);        yield return www;         www.assetBundle.Unload(false);    }    // 加载并实例化    IEnumerator LoadAndInstantiate(string url, int version)    {        WWW www = WWW.LoadFromCacheOrDownload(url, version);        yield return www;        if (!System.String.IsNullOrEmpty(www.error))        {            Debug.Log(www.error);        }        else        {            Object main = www.assetBundle.mainAsset;            GameObject.Instantiate(main);        }        www.assetBundle.Unload(false);    }</span></span>

       从上面的代码可以看出来,如果很依赖关系比较复杂的话,不仅分析起来麻烦而且代码量也会很多,但是在2015Unity发布会上,有一位开发者提出了这样一个解决方法:链接地址:http://forum.china.unity3d.com/thread-7197-1-1.html    就是利用构建有向图,找到入度为0和入度大于2的节点,然后在深度优先遍历,可是貌似实现起来蛮困难的,话说我大一学的算法现在都忘得差不多了,在这里就不实现了大笑


最后的最后:

      AssetBundle的四种加载方式的对比:

    1.new www()   是通过一个路径进行下载(无论是服务器路径还是本地路径下载操作都一样)但是bundle只能保存在内存中,也就是退出游戏在进入还得重新下,同时AssetBundle默认是压缩格式的,每次下载的时候都需解压缩,这样就会使得其加载比较缓慢。

     2.WWW.LoadFromCacheDownload()  页游和网游比较适合,第一次下载解压缩后都会保存在Cache缓存中,这样下次下载就不需解压缩了,速度很快,但是很占用内存,同时他是个异步操作,在有些情况下会有错,比如用户打开某个界面时UI某个节点下 load模型 。可是用户手比较快,把界面关了。 这样异步加载回来如果不处理肯定就出错了。所以类似这样的地方异步都要处理, 但是同步就没这问题。

     3.AssetBundle.CreateFromMemory() 这个是从内存中下载,一般来说在文件需要加密和解密的时候会用。

     4.AssetBundle.CreateFromFile() 这个加载速度很快,同时也是个同步操作。

 所以建议大家使用AssetBundle.CreateFromFile()。




0 0
原创粉丝点击