其他
Unity项目资源加载与管理
这也是开发者经常会遇到的问题。一般情况都是,开发者先把被依赖AssetBundle中的资源加载出来,然后用Unload卸载掉这个被依赖的AssetBundle,最后发现从其它AssetBundle中加载出来的GameObject丢失了前面加载出来的资源。
那么现在我们知道,其实通过LoadAsset从被依赖资源的AssetBundle中加载出来的资源,除非我们手动绑定回主体资源中(例如把加载出来的材质通过代码绑定回GameObject的Renderer中),否则是不会被自动索引到的。
也就是说,通过AssetBundle来动态加载资源时,我们并不需要自己加载被依赖的资源,而是只要保证主体在加载时被依赖资源所在AssetBundle依然处于开启状态就可以正常加载资源了。
大家都应该知道,粉色材质在Unity中是Shader出错或丢失时的默认警示材质,而在这种情况下就是Shader丢失的问题。
这时,如果原来的Shader和Texture资源是从AssetBundle中加载出来的,并且该AssetBundle已经被卸载掉的话,那么Unity就无法再从内存中加载到这些资源,从而导致GPU丢失Shader与Texture了。
可能有朋友会说,这些Shader和Texture不是曾经被加载到内存中吗?是的,但是它们在被加载到GPU之后会被从内存中清除掉。因此要防止这种问题的发生最稳健的办法就是Shader和Texture的AssetBundle在场景切换前都不要卸载掉。而如果担心AssetBundle本身消耗内存问题的话可以参考下一个问题的解答。
Unity默认的AssetBundle压缩方式是LZMA,这种压缩方式的AssetBundle在加载到内存时会马上执行解压过程,并在AssetBundle处于开启状态期间在内存中保留一个解压过后的Cache(例外情况:UnityWebRequest.GetAssetBundle与WWW.LoadFromCacheOrDownload均会在第一次解压LZMA AssetBundle时把解压后版本Cache到文件系统中,后续的调用就无需在内存中保留解压后的AssetBundle了),因此内存占用会比较明显。
而Unity5.3以后提供的ChunkBasedCompression是一种基于Chunk的LZ4压缩方式,这种压缩方式可以让AssetBundle对单独的Asset进行压缩,而不是AssetBundle整体压缩。因此加载这种压缩方式的AssetBundle时,无需事先解压,只需在内存中保留一个头结构,然后在加载某个Asset的时候才即时从文件中读取该Asset所在的chunk并解压到内存中。
由于LZ4是公认的解压速度极快的压缩方式,并且需要即时解压的数据量一般不会很大,因此实时解压Asset带来的CPU时间消耗其实很小,另一方面把解压时间分散在不同地方也减轻了一次性解压带来的卡顿问题。当然,最重要的优点还是在于大大减轻了内存的压力,可以放心地让有可能被再次索引的AssetBundle常驻在内存中,因此我们非常推荐大家使用LZ4压缩方式的AssetBundle。
不过大家在使用过LZ4压缩方式后应该也会发现,在资源数比较多的情况下,LZ4格式的AssetBundle大小基本都要比LZMA格式的大一些,这也是分块压缩不可避免的缺陷。但考虑到它的内存消耗表现优秀,移动端的开发朋友应该可以忽略这一点吧。
要理解这个问题,我们需要知道Unity程序启动时的一个操作。在启动加载的过程中,Unity需要为Resources文件夹(此时其实就是一个序列化文件)中的所有资源构建一个查找树作为后面加载具体Asset时所需的索引数据,而这个结构的构造时间是非线性的(比线性稍高一点),因此在Resources文件夹中的文件越多,启动加载的时间就越明显,基本10000个资源在一些低端手机上需要5到10秒的加载时间。
另一方面,考虑到Resources文件夹无法动态更新,也没有AssetBundle Variant这种设定不同资源版本的功能,因此我们极力推荐大家主要采用AssetBundle进行资源的动态加载,而Resources文件夹的使用可以只考虑这几种情况:
这些资源在整个游戏的运行期间都会用到;
这些资源无需为不同平台或硬件适配定制资源;
这些资源无需动态更新;
正在制作游戏的原型。
这种问题产生的根源在于从AssetBundle中加载出来的资源,在该AssetBundle卸载之后与此AssetBundle的联系就断开了。举个例子,我从AssetBundle A 中加载出来一个Prefab p1, 那么p1本身依赖的资源,例如一个Texture tex1也会自动加载到内存中。然后我用AssetBundle.Unload(false)来卸载AssetBundle A,此时p1与AssetBundle A已断开关系。之后过了一段时间,我需要从AssetBundle A中加载另一个Prefab p2 ,假设p2也依赖于Texture tex1,那么我再次加载AssetBundle A,从中加载p2时tex1会再次被加载到内存中,导致此时内存中存在两份tex1。
关于AssetBundle的常见问题解答就分享到这里,如果还有其它疑问,也可在下方评论区留言,或访问Unity官方中文社区(forum.china.unity3d.com)发帖提问。
Unity官方活动
请点击活动标题,了解活动详情,报名参与!
活动时间:7月29日 9:00 ~ 18:00
活动地点:上海淳大万丽酒店
活动内容:实战演练Unity 2D与3D游戏开发!
活动时间:7月28日 9:00 ~ 12:00
活动地点:上海浦东嘉里大酒店
活动内容:聚焦女性在游戏行业的发展与未来!
ChinaJoy期间我们还有“两岸开发者交流会”、“Unity 官方认证考试”等更多精彩主题活动,详情请看:Unity官方活动全推荐!
我们还会分享更多Unity技术答疑内容在Unity官方中文论坛(forum.china.unity3d.com),请保持关注!