Unity5 怎样做资源管理和增量更新

工具

Unity 中的资源来源有三个途径:一个是Unity自己主动打包资源。一个是Resources。一个是AssetBundle。

  • Unity自己主动打包资源是指在Unity场景中直接使用到的资源会随着场景被自己主动打包到游戏中。这些资源会在场景载入的时候由unity自己主动载入。这些资源仅仅要放置在Unityproject目录的Assets目录下即可,程序不须要关心他们的打包和载入,这也意味着这些资源都是静态载入的。

    但在实际的游戏开发中我们一般都是会动态创建GameObject。资源是动态载入的,因此这样的资源事实上不多。

  • Resources资源是指在Unityproject的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载入,仅仅有当有一个增量版本号时,游戏主程序才会去server把增量的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一起全部拷贝到server上。

同一时候在Resources目录下也存一份,随完整安装包公布,这就保证了新安装游戏的玩家手机上也有一份完整的资源配置文件,记录了这个完整包包括的资源。

当游戏启动时。首先请求server检查版本号号。前端用的版本号号就是Resources以下的这个resourcesinfo中的version。

server比对这个版本号号来告诉前端是否须要更新。假设须要更新。前端就去获取server端的新resourcesinfo。然后比对里面每一个bundle的HashCode,把HashCode不同的bundle记录下来。然后通过WWW类来下载这些发生改变的bundle,当然假设server版的resourcesinfo中包括了本地resourceinfo中所没有的Bundle,这些Bundle就是新增的。也须要下载下来。全部下载完毕后。前端将这个新的resourceinfo保存到本地存储中。后面前端的全部操作都将以这个resourceinfo为准而不再是Resources以下的resourceinfo了。Resources下的resourceinfo能够退出历史舞台了。除非一种情况:本地存储的resourceinfo被觉得删除了。

手机端玩家清理应用的数据就会造成下载的bundle以及resourceinfo被删除。没关系,这时候前端由于找不到外部的resourceinfo了,还会使用Resources以下的resourceinfo和server比对。把新的bundle又一次下载下来。

如今从哪里载入资源就非常明白了:ResourceMananger先读取resourcesinfo。知道了游戏中全部的Bundle和每一个Bundle包括的资源,然后去外部存储查找这些Bundle是否存在,假设存在,就记录下这个Bundle的资源应该从外部的AssetBundle载入,假设不存在,就从内部的Resources载入。在开发过程(Editor)中,由于不存在外部存储的bundle,资源自然都是从Resources载入的,达到了我们开发方便的目的。这个过程隐含了一点:不是全部的资源都须要有BundleName而被打包到AssetBundle中,游戏内不须要兴许更新的资源就不要设置BundleName。它们不会被打包更新,这样的资源ResourceManager在resourceinfo中是找不到的,直接去Resources目录以下读取即可了。

载入AssetBundle,我们直接使用WWW类而不用WWW.LoadFromCacheOrDownload, 由于我们的资源在游戏開始的时候已经下载到外部存储了。不要再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。

原文地址:http://blog.csdn.net/ring0hx/article/details/46376709

时间: 2024-10-15 08:40:30

Unity5 怎样做资源管理和增量更新的相关文章

Unity5 如何做资源管理和增量更新

工具 Unity 中的资源来源有三个途径:一个是Unity自动打包资源,一个是Resources,一个是AssetBundle. Unity自动打包资源是指在Unity场景中直接使用到的资源会随着场景被自动打包到游戏中,这些资源会在场景加载的时候由unity自动加载.这些资源只要放置在Unity工程目录的Assets文件夹下即可,程序不需要关心他们的打包和加载,这也意味着这些资源都是静态加载的.但在实际的游戏开发中我们一般都是会动态创建GameObject,资源是动态加载的,因此这种资源其实不多

【转载】Unity 合理安排增量更新(热更新)

原帖地址:由于我看到的那个网站发的这篇帖子很大可能是盗贴的,我就暂时不贴地址了.避免伤害原作者 原版写的有点乱,我个人修改整理了下. ---------------------------------------------------------------------------------------------------- 工具 Unity 中的资源来源有三个途径:一个是Unity自动打包资源,一个是Resources,一个是AssetBundle. Unity自动打包资源是指在Uni

谈谈混合 App Web 资源的打包与增量更新

综述 移动 App 的运行环境具有带宽不稳定,流量收费,启动速度比较重要等特点,所以混合 App 如何加载 Web 资源并不是一个新问题.本文目的是总结出一种资源打包下载的思路和方案,并且提供一种打包工具.本文提到的思路只是一家之言,基本没有参考现有方案,各位方家有不同意见欢迎留言.另外本文没有涉及到 App 内部如何加载资源的问题,这部分我会专门撰写一篇文章讨论. 需求梳理 一般来说,Hybrid-app 对于 Web 资源下载有如下需求: 页面开启速度要快,所以资源的下载和使用不是在同一时间

一步一步跟我学习lucene(19)---lucene增量更新和NRT(near-real-time)Query近实时查询

这两天加班,不能兼顾博客的更新,请大家见谅. 有时候我们创建完索引之后,数据源可能有更新的内容,而我们又想像数据库那样能直接体现在查询中,这里就是我们所说的增量索引.对于这样的需求我们怎么来实现呢?lucene内部是没有提供这种增量索引的实现的: 这里我们一般可能会想到,将之前的索引全部删除,然后进行索引的重建.对于这种做法,如果数据源的条数不是特别大的情况下倒还可以,如果数据源的条数特别大的话,势必会造成查询数据耗时,同时索引的构建也是比较耗时的,几相叠加,势必可能造成查询的时候数据缺失的情况

【安卓】数据库基于脚本的"增量更新",每次更新时不需修改java代码、!

思路: 1.当然是基于SQLiteOpenHelper.onCreate(第一次安装程序时调用).onUpdate(升级程序时调用) 2.用"脚本"(脚本制作具体方法问度娘)做数据库升级,文件名标识对应版本,java中根据"上一版本.当前版本"选择执行的脚本. 升级时,修改DB_VERSION(当前版本)即可. DBManager.java: package com.example.test; import java.io.ByteArrayOutputStream

一个简单的数据增量更新策略(Android / MongoDB / Django)

我在做个人APP - CayKANJI - 的时候遇到一个问题: 怎样增量式地把日语汉字数据地从服务器更新到APP端,即每次用户执行更新操作时,只获取版本高于本地缓存的内容. 数据格式 为了能够与mongoDB无缝结合,并省去编写后台代码的麻烦,索性就把汉字数据保存成json文件,上传到服务器后,交给web应用去读取并写入数据库. 汉字文件就是普通的json格式. { "category": "行為ー2", "contents": [ { &qu

最全的增量更新入门 包含linux端和Android

简介 增量更新大量用于 Android各大应用市场.本文想做网络上从服务器到app客户端完整讲解.app用eclipse和android studio 最新版cmark开发ndk 如下图: 以前一直好奇怎么做的直到知道了bsdiff库. 地址附上: bsdiff源码地址和简介 大家可以从简介看到bsdiff是基于bzip2源码(bsdiff和bspatch一个用于生成差异文件补丁,另一个用于差异文件和旧文件合成新文件) 下载地址说明 应用市场原理说明 假设你用的是"XXX市场"点击更新

`cocos2dx非完整` 日志模块 增量更新

在上一篇文章中,说到了"流程"的由来,以及我对流程的使用. 这一片就是对流程的应用.前一篇文章中说到了三条流程 check_log_measure, check_env_measure, check_update_measure.先来看看chenck_log_measure的源码: 1 --小岩<[email protected]> 2 --2015-5-28 1:29 3 local clm = class("check_log_measure", f

Android 增量更新实例(Smart App Updates)

目录[-] 官方说明 实现原理 实现 (1)生成差异包 (2)使用旧apk+差异包,在客户端合成新apk 注意事项 demo 自从 Android 4.1 开始,Google引入了应用程序的增量更新. 官方说明 Smart app updates is a new feature of Google Play that introduces a better way of delivering app updates to devices. When developers publish an