Unity AssetBundle,Asset,GameObject之间的联系
一.问题
首先,这里说明一下,我这边的GameObject有点笼统,就是表达的是游戏中的具体实例。
二.概念
1)Asset是什么?
游戏中具体的资源,像texture,mesh,material,shader,script等,实实在在的游戏项目文件夹中所需要堆放的资源。比如,var obj = Resource.Load<GameObject>("Prefabs/testItem"),这个obj就是Asset。
2)GameObject是什么?
var gameItem= Instantiate(obj),这个gameItem就是可以存在于游戏的实际场景中(这个比较简单,不多说了)。GameObject是游戏中实际使用的对象(就是你会在屏幕中实际看到的),是由Asset实例化后的对象。本质上其实还是Asset的衍变,是对部分Asset的引用和复制出来的新东西,其本质还是Asset。
3)AssetBundle是什么?
由上述可知,我们在游戏中生成实际的物体,需要Asset。而Asset,比如一张图片,也是一个Asset,实际大小1M,游戏中这种图片很多,那就轻轻松松几百M,几个G的Asset,都是很有可能的。作为程序员,对于这种“原汁原味”拿过来肯定不行。比如我们工作中把文件什么的发给同事的时候都知道压缩一下,可以传输的过程中小一些。当然了,我们在游戏开发中使用Asset,也是需要类似的。于是,就推出了AssetBundle这一概念。当然我们推出AssetBundle,远不止压缩这一需求。但是你需要知道,主要是为了更好的传输,还有比如减少资源大小,利于网络那边的传输,方便加载。
简而言之,AssetBundle就是为了让游戏项目中大量Asset适应实际游戏运行时而被压缩后的一种二进制文件。
三.分析
1)Asset和GameObject的关系?
①复制+引用关系
Instaniate一个Prefab(Asset),是对Asset进行clone(复制)+引用结合的过程。GameObject,transform是clone的。其他mesh/texture/material/shader等,这些都是纯引用的。引用的Asset对象不会被复制,只是一个简单的指针指向已经load的Asset对象。专门要提一下Script Asset,Unity里每个Script都是一个封闭的class定义,并没有写调用代码。光class是不会工作的。其实Unity引擎就是那个调用代码。clone一个script asset等于new一个class实例,实例才会工作 ,把它挂到Unity主线程的调用链去,class实例里的update和start才会被执行。多个物体挂同一个脚本,其实就是多个物体挂了挂了那个脚本的类的实例。在new class过程中,数据区是复制的,代码区是共享的,算是一种复制+引用关系。引用关系的话,会有一对一,一对多,多对一的关系。如下图:
②释放
如果你Destroy(GameObject,这个是游戏中具有实例),只是释放了clone的asset,引用的asset并不会被干掉。还有因为destroy并不知道有没有被别的asset引用。但是如果你想把asset也释放,有两种方案。
I. Resources.UnloadUnusedAssets()
释放当前所有的没有被引用的(无用)asset,但是不能保证释放掉当前的被引用的资源(因为可能还被其他资源引用,就不会去释放了)。缺点:异步,会卡。由于Unity资源的相互引用关系比较复杂,想要明确判断某一资源不存在引用关系是有一定难度的,并且,如果我 们想要释放的资源存在隐形的引用关系,UnloadUnUsedAssets将会无视这个资源而无任何反馈。根据实战来看,最佳使用的时机是在场景切换进入新的场景后,Unity场景关闭会有效的销毁所有的对象和所有代码的引用,即在新场景开头最为稳妥。必要时加上GC.Collect().
II.Resouce.UnLoadAsset(obj)
释放当前实例的所有被引用的asset(不管这个asset是否还有被引用,所以,风险很大,除非保证确定被他引用的资源没有再被其他资源引用,一般用于单独的一张纹理释放)缺点:风险太大,容易被报:UnloadAsset may only be used on individual assets and can not be used on GameObject's / Components or AssetBundles)用于释放shader,mesh,Texture等,没有被引用的。当然如果GameObject没有被引用,你也可以去释放。
对于这种使用,对于资源大的且无引用的可以使用,如果资源消耗不大,可以等到场景切换,使用I中 Resources.unloadUnusedAssets方案。
III.GameObject.DestroyImmediate(asset,true)
代替上述方案,可以针对卸载asset(好像还可以直接使用GameObject.Destroy(asset) ,也可以直接使用。(待验证))
IV.AssetBundle.unload(true) !!!慎用
这个也可以卸载asset,但是卸载的是这个assetBundle里面的所有的asset。(后面详说)
2)AssetBundle和Asset的关系
①包含,依赖关系
一个AssetBundle中可以包含一个或多个Asset。一个Asset依赖于AssetBundle。
②释放
AssetBundle的释放只能通过以下两种方式释放。即使系统在加载新场景的时候所有的内存对象都会被自动销毁,包括你用AssetBundle.load加载的对象和Instaniate克隆的,但是不包括AssetBundle文件本身的内存镜像,那个必须要用Unload来释放,用.Net术语说该资源是非托管的。
I.AssetBundle.Unload(false) 使用频率较多
用AssetBundle.Load加载需要的asset之后应该立即使用unlaod(false),释放assetbundle文件本身的内存镜像,但不销毁该assetBundle加载过的asset对象。(尽量释放一部分内存,大多数游戏这么做)
II.AssetBundle.Unload(true)
释放该assetBundle文件镜像并释放该assetBundle所有loaded的asset内存对象。(风险很大,因为一般不太能确定是否该loaded的asset是否还被其他资源引用)。
四.实例测试
AssetBundle和Asset 项目工程中大小分析
①首先,准备10个一样大小的texture(为了测试结果更加准备,每个图1.33M)
texture 的原大小,即Asset本身的大小:1.33M *10 = 13.3M
②LZ4和LZMA打包方式案例对比
I.BuildAssetBundleOptions.None 打包方式,该为默认压缩,即LZMA,在使用AssetBundle之前需要解压缩。使用LAMA格式压缩的AssetBundle的包体最小
(高压缩比),但是会增加相应的解压缩时间和内存。
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None,BuildTarget.StandaloneWindows64);
以上述方式打包完之后 每张 texture :613K *10 = 6M
执行下面代码。(为方便数据对比,是在找不到大的texture了)
使用AssetBundle.LoadFromFile 加载那10个AssetBundle之后的内存显示
对比上2张图的消耗,大概消耗了13.6M的Unity内存,0.5M其他内存。因为是十张纹理,原纹理没有打包之前的大小大概是1.33m,也就是说,大概技术释放的是原Asset的大小。
再Load其中的Asset
由上图可知,申请的是Unity内存爆增100M左右,Mono的内存也相应的增加了。
II.ChunkBasedCompression,即LZ4压缩方式,压缩比一般,压缩后的包体较大,但是解压速度快,消耗内存小。
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.ChunkBasedCompression,BuildTarget.StandaloneWindows64);
以上述方式打包完之后 每张texture :1110K*10=11100K,11M左右
再执行同上一致的Load脚本.
AssetBundle,Asset,GameObject 加载中的内存对比
同上述步骤一致,给出LoadAssetBundle,即AssetBundle.LoadFromFile后的内存大小
大概消耗2M内存。(上述LAMA方式打出的包是23.5M,相差10倍啊,果断使用这种方式,我们项目也是这种方式)
再给出LoadAsset之后的内存图
LoadAsset之后,Unity内存,Mono内存都增加很多
III.总结对比,I加载的时候LoadAssetBundle消耗明显大于II方式10倍(甚至不止,因为总量越大,差距就越大)。移动游戏中的内存多么珍贵大家懂得。使用ChunkBasedCompression,即LZ4压缩,更为划算。因为宁愿牺牲一点包体大小,也要消耗内存小一些。(我们游戏项目是这个需求,具体还是看项目吧)
当然还有其他打包选项,各有利弊,具体看游戏实际情况需求。我这里只是对比出了告诉你打包选项会决定你的打出的资源包体大小和游戏中加载消耗内存。很重要。
具体打包选项BuildAssetBundleOptions参考:https://blog.csdn.net/AnYuanLzh/article/details/81485762
补充:由上,我们知道打包时压缩方式会导致打出的包体和加载时的消耗都不同。加载时候的加载方式我们这边使用AssetBundle.CreateFromFile直接加载AssetBundle,Unity其实还提供了WWW加载AssetBundle的方式。但是这里不作详述了(讲不完...)。
五.总结
由上可知,文章虽说AB,Asset,GameObject三者联系,但是GameObject主要是由Asset实例化而来,GameObject是Asset的引用和复制的关系(主要引用),这个也可以说是Asset的一种。问题也就可以简化为AB和Asset之间的关系。
由上图,再总结一下,打包方式不同,加载方式不同,造成的消耗不同。即,如果想优化游戏中的资源,需要注意打包AB的方式,以及加载AB的方式。当然,还要注意AB的卸载,和Asset的卸载。
六.参考
关于加载AssetBundle和加载Asset的区别,详情就不多说了,见UWA:https://blog.uwa4d.com/archives/ABTheory.html
打包加密压缩算法区别参考:https://www.cnblogs.com/murongxiaopifu/p/5629415.html#autoid-3-3-0
附:测试Demo源码:链接:https://pan.baidu.com/s/1ZHPoQbuxgdUVh9PGbz6ArA 提取码:7lkv
Unity AssetBundle,Asset,GameObject之间的联系的更多相关文章
- [Unity AssetBundle]Asset资源处理
什么是AssetBundle 在很多类型游戏的制作过程中,开发者都会考虑一个非常重要的问题,即如何在游戏运行过程中对资源进行动态的下载和加载.因此,Unity引擎引入了AssetBundle这一技术来 ...
- 【Unity3D技巧】在Unity中使用事件/委托机制(event/delegate)进行GameObject之间的通信 (二) : 引入中间层NotificationCenter
作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 一对多的观察者模式机制有什么缺点? 想要查看 ...
- Unity AssetBundle
Unity AssetBundle爬坑手记 - 夜阑卧听风吹雨 时间 2014-09-15 16:55:00 博客园精华区原文 http://www.cnblogs.com/ybgame/p/39 ...
- Unity AssetBundle笔记
1.入门: Resources:表示U3D自动将资源打成一个AssetBundle包,所有放在Resources下的文件夹都会打成一个AssetBundle包,资源非常大,Resources文件夹在真 ...
- Unity AssetBundle 踩坑记录
Unity AssetBundle 踩坑记录 editor 下选择什么平台的 ab 加载 Material doesn't have a color property '_Color' UnityEd ...
- 【Unity3D技术文档翻译】第1.9篇 使用 Unity AssetBundle Browser tool (AssetBundle系列完结)
上一章:[Unity3D技术文档翻译]第1.8篇 AssetBundles 问题及解决方法 本章原文所在章节:[Unity Manual]→[Working in Unity]→[Advanced D ...
- Unity AssetBundle爬坑手记
这篇文章从AssetBundle的打包,使用,管理以及内存占用各个方面进行了比较全面的分析,对AssetBundle使用过程中的一些坑进行填补指引以及喷! AssetBundle是Unity推荐的 ...
- (转)Unity AssetBundle爬坑手记
转自:http://www.cnblogs.com/ybgame/p/3973177.html 这篇文章从AssetBundle的打包,使用,管理以及内存占用各个方面进行了比较全面的分析,对Asset ...
- Unity AssetBundle 游戏资源分类及关系
--刚刚做完一个xlua的的热更项目,对AssetBundle资源分类总结一下.纯理论,闲谈知识,要是有建议,尽管提 ,不掺杂代码. --这里说说,AB是如何打包,如果下载,如何加载. 1.关键词理解 ...
随机推荐
- 深度神经网络(DNN)
深度神经网络(DNN) 深度神经网络(Deep Neural Networks, 以下简称DNN)是深度学习的基础,而要理解DNN,首先我们要理解DNN模型,下面我们就对DNN的模型与前向传播算法做一 ...
- Arcgis for Javascript实现图
首先,截个图给大家看结果: 初始化状态 放大后的状态 点击选中后的状态 如上图所看到的,一般的涉及到的地图的统计涉及到上述所展示的三个状态:1.初始化状态.2.缩放后的状态:3.点击选中显示详情状态. ...
- vc有关 directx组态,和dxsdk_extras(directshow)
2009-11-10 0:28 此文章:自己编写 转载于<汤姆&杰瑞> DirectShow 1 -- 下载与VC配置 1 DirectX SDK9 Directshow sd ...
- 【转】postgresql 9.4 在linux环境的安装步骤详解
本文章来为各位介绍一篇关于postgresql 9.4 在linux环境的安装步骤详解,希望文章能够对各位新手朋友带来帮助的哦. 环境说明系统:centos 6.4 64位软件:postgresql ...
- abp框架(aspnetboilerplate)设置前端报错显示
abp在后端抛出异常 throw new UserFriendlyException($"抛出一个错误"); 在发布之前,需要设置是否把报错发送给前端 如果将此设置为true,则会 ...
- Win8 Metro(C#) 数字图像处理--1 图像打开,保存
原文:Win8 Metro(C#) 数字图像处理--1 图像打开,保存 作为本专栏的第一篇,必不可少的需要介绍一下图像的打开与保存,一便大家后面DEMO的制作. Win8Metro编程中,图像相关 ...
- SQL Server 2016新特性:DROP IF EXISTS
原文:SQL Server 2016新特性:DROP IF EXISTS 在我们写T-SQL要删除某个对象(表.存储过程等)时,一般会习惯先用IF语句判断该对象是否存在,然后DROP,比如: 旧 ...
- 内存页面的各种属性(就是Read, Write, Execute的组合)
PAGE_NOACCESS 禁止写入执行读取查看进程内存区域能发现,NOACCESS属性的内存页面都是FREE状态的(未提交使用的内存区域),只有内存区域最后的0x7FFE1000-0x7FFF000 ...
- .gitignore 配置后无效
利用.gitignore过滤文件,如编译过程中的中间文件,等等,这些文件不需要被追踪管理. 现象: 在.gitignore添加file1文件,以过滤该文件,但是通过Git status查看仍显示fil ...
- ORA-09925: Unable to create audit trail file
当我修改ORACLE_SID为新的SID,想进行数据库还原时,用sqlplus报如下错误 [oracle@dbtest ~]$ sqlplus / as sysdba SQL Production : ...