Unity 热更新之AssetBundle

前言:

  这几天刚好看到了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
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值