东西
Unity 中的资源来历有三个途径:一个是Unity主动打包资源,一个是Resources,一个是AssetBundle。
• Unity主动打包资源是指在Unity场景中直接运用到的资源会跟着场景被主动打包到游戏中,这些资源会在场景加载的时分由unity主动加载。这些资源只需放置在Unity工程目录的Assets文件夹下即可,程序不需求关怀他们的打包和加载,这也意味着这些资源都是静态加载的。但在实践的游戏开发中咱们一般都是会动态创立GameObject,资源是动态加载的,因而这种资源其实不多。
• Resources资源是指在Unity工程的Assets目录下面能够建一个Resources文件夹,在这个文件夹下面放置的一切资源,不管是否被场景用到,都会被打包到游戏中,并且能够经过Resources.Load办法动态加载。这是平常开发是常用的资源加载办法,可是缺陷是资源都直接打包到游戏包中了,无法做增量更新。
• AssetBundle资源是指咱们能够经过编辑器脚原本将资源打包成多个独立的AssetBundle。这些AssetBundle和游戏包是别离的,能够经过WWW类来加载。AssetBundle的运用很灵敏:能够用来做分包发布,例如大多数页游资源是跟着游戏的进程增量下载的,或许有些手游资源过大,途径要求发布的包约束在100M以内,那只能把一开端玩不到的内容做成增量包,等玩家玩到的时分经过网络下载。AssetBundle 也能够用来做咱们下面评论的主动增量更新。
Unity5比较之前的版别,AssetsBundle的打包进程有所简化。之前打包需求经过代码来设置需求打入包的资源并自己树立包的依靠联系,Unity5能够经过每个资源Inspector底部的AssetBundle下拉来指定该资源要打入哪个包,不指定便是不打包。打包进程只需求BuildPipeline.BuildAssetBundles一句话就行了,Unity5会依据依靠联系主动生成一切的包。每个包还会生成一个manifest文件,这个文件描绘了包巨细、crc验证、包之间的依靠联系等等,经过这个manifest打包东西在下次打包的时分能够判别哪些包中的资源有改动,只打包资源改动的包,加快了打包速度。manifest仅仅打包东西自己用的,发布包的时分并不需求。
关于主动生成依靠联系这个有必要提下,Unity的确会主动给你树立依靠联系,条件是你依靠的资源有必要现已在Inspector中设置了BundleName。假如没有,Unity会把这个共用的资源重复打到多个用到的包中,由于这个共用资源不在一个独立的包中,Unity也不会智能地给你发现它是共用的,然后生成一个共用包。更深的坑在于,假如你共用的是一个FBX模型,你只给这个模型设置BundleName还不可,它用到的贴图,原料都要设,不然模型是共用了,贴图没有共用,成果贴图仍是被打包到多个包中了。所以设置BundleName这个作业最好仍是由编辑器脚原本完结。
计划
Unity供给的就这些了,下面就自己发挥:怎么做一个便利的资源办理计划,既能够开发时便利,又能够便利发布更新包呢?开发进程全用AssetsBundle是不合适的,由于开发中资源常常增加和更新,每次增加或许更新都生成一下AssetsBundle才干运转是很费事的。并且咱们要做的是主动更新而不是分包下载,这也便是说在发布游戏的时分这些资源应该都是在游戏包中的,所以他们也不应从AssetsBundle加载。
剖析完需求,计划也就出来了:资源仍是放在Resources下面,可是这些资源一同也会打包到AssetBundle中。代码中一切加载资源的当地都经过自己的ResourceManager来加载,由ResourceMananger来决定是调用Resources.Load来加载资源仍是从AssetsBundle加载。在开发环境下(Editor)这些资源显然是直接从Resources加载的,发布的完好装置包资源也是从Resources加载,只有当有一个增量版别时,游戏主程序才会去服务器把增量的AssetBundle下载下来,然后从AssetBundle加载资源。
完成
完成中咱们首要要考虑的是AssetBundle的粒度,即每个AssetBundle包括多少资源。增量包的最小粒度便是AsssetBundle, 假如单个AssetBundle过大,只需这个AssetBundle中有一个资源改动了就需求从头下载整个AssetBundle,糟蹋流量和玩家的等待时刻;假如单个AssetBundle过小,极点状况是每个资源一个AssetBundle,尽管完成了更新最小化,可是带来了额定开支:AssetBundle自身也是有巨细的,并且查找加载AssetBundle也是需求时刻的。咱们都往U盘里边拷过东西,拷一个1G的文件比拷1千个1M的文件要快许多。比较合理的做法是依据逻辑来,例如每个人物能够有独立的AssetBundle,共用的一些UI资源能够打到一个AssetBundle里边,每个场景独立的UI资源能够打成独立的AssetBundle。这样做资源预加载的时分也便利,每个场景需求用到几个Bundle就加载几个Bundle,无关的资源不会被加载。
下面要考虑的是怎么来确认一个资源是从Resources加载仍是AssetBundle加载。为此咱们需求一个配置文件resourcesinfo。这个文件随打包进程主动生成。里边包括了资源版别号version,一切包的姓名,每个包的HashCode以及每个包里边包括的资源的姓名。HashCode直接能够从Unity生成的manifest中得到(AssetBundleManifest.GetAssetBundleHash),用来查看包的内容是否产生变化。这个resourceinfo每次打包AssetBundle时都会生成一个,发布增量时将它和新的Bundle一同悉数仿制到服务器上。一同在Resources文件夹下也存一份,随完好装置包发布,这就确保了新装置游戏的玩家手机上也有一份完好的资源配置文件,记录了这个完好包包括的资源。
当游戏启动时,首要恳求服务器查看版别号,前端用的版别号便是Resources下面的这个resourcesinfo中的version。服务器比对这个版别号来告知前端是否需求更新。假如需求更新,前端就去获取服务器端的新resourcesinfo,然后比对里边每个bundle的HashCode,把HashCode不同的bundle记录下来,然后经过WWW类来下载这些产生改动的bundle,当然假如服务器版的resourcesinfo中包括了本地resourceinfo中所没有的Bundle,这些Bundle便是新增的,也需求下载下来。一切下载完结后,前端将这个新的resourceinfo保存到本地存储中,后边前端的一切操作都将以这个resourceinfo为准而不再是Resources下面的resourceinfo了。Resources下的resourceinfo能够退出历史舞台了,除非一种状况:本地存储的resourceinfo被以为删去了。手机端玩家整理运用的数据就会形成下载的bundle以及resourceinfo被删去。没联系,这时分前端由于找不到外部的resourceinfo了,还会运用Resources下面的resourceinfo和服务器比对,把新的bundle从头下载下来。
现在从哪里加载资源就很清晰了:ResourceMananger先读取resourcesinfo,知道了游戏中一切的Bundle和每个Bundle包括的资源,然后去外部存储查找这些Bundle是否存在,假如存在,就记录下这个Bundle的资源应该从外部的AssetBundle加载,假如不存在,就从内部的Resources加载。在开发进程(Editor)中,由于不存在外部存储的bundle,资源天然都是从Resources加载的,达到了咱们开发便利的意图。这个进程隐含了一点:不是一切的资源都需求有BundleName而被打包到AssetBundle中,游戏内不需求后续更新的资源就不要设置BundleName,它们不会被打包更新,这样的资源ResourceManager在resourceinfo中是找不到的,直接去Resources文件夹下面读取就行了。
加载AssetBundle,咱们直接运用WWW类而不必, 由于咱们的资源在游戏开端的时分现已下载到外部存储了,不要再Download也不要再Cache。留意WWW类加载是异步的,在游戏中咱们需求同步加载资源的当地就要留意把资源预加载好存在ResourceManager中,不然等用的时分加载必定要写异步代码了。大部分时分咱们应该在一个场景初始化时就预加载好一切资源,用的时分直接从ResourceManager的缓存取就能够了。
资源加载卸载
最终简略说下资源的加载卸载,这个网上也有许多文章介绍。
从我了解来看Resources是一个缺省主动打包的特别AssetBundle。不管从WWW仍是AssetBundle.CreateFromFile创立AssetBundle其实是创立了一个文件内存镜像。这时分是没有Asset的。AssetBundle.LoadAsset 和Resource.Load才真实创立出了Asset,而Instaniate仿制了这个Asset。留意这个仿制有两种,学C++的都知道浅仿制和深仿制,这儿的仿制有的是正真的仿制,有的是引证。为什么要这样呢?由于有些游戏资源是只读的,像贴图Texture,这么大并且只读,当然不需求再去彻底仿制一份。但像GameObject这种资源它的特点是能够经过脚本改动的,有必要要仿制一份。所以一个资源从AssetBundle到场景中被实例化,其实有3块内存被创立,这3快内存的开释是有不同办法的。
文件内存镜像是经过AssetBundle.Unload(false)来开释的。
Instaniate出来的Object内存经过Object.Destory来开释。
AssetBundle.Unload(true)不单会开释文件内存镜像,还会开释AssetBundle.Load创立的Assets。这个办法是不安全的,除非你能确保这些Assets没有Object在引证,不然就出问题了。
Resources.UnloadAsset和Resources.UnloadUnusedAssets能够用来开释Asset。
下面这个图很直观:
这是老Unity的图,Unity5现已把AssetBundle.Load 改成了AssetBundle.LoadAsset。这个改动让咱们更清晰了Load出来的是Asset这块内存区域。什么时分把Resource.Load也改了吧。
留意事项
• Resources.Load办法传入的资源途径需是从Resources文件夹下一级开端的相对途径且不能包括扩展名;而AssetBundle.LoadAsset办法传入的资源名需是从Assets文件开端的全途径且要包括扩展名。途径不区别巨细写,主张全用小写,由于AssetBundle.GetAllAssetNames办法回来的资源名都是小写的。
• Unity5打包AssetBundle时会主动处理依靠联系,可是在运转时加载的时分却不会,程序需求自己处理,先加载依靠包。
• AssetBundle.CreateFromFile不能加载紧缩过的AssetBundle,所以咱们只能用WWW来异步加载AssetBundle。
• 现在我用的Unity5.0.2f1的Resources.Load办法在手机端比本来慢了许多,假如曾经能够不缓存每次用的时分都调用Resource.Load现在就不可了。频频的调用会导致显着的功能开支,不知道是不是Bug。