【Unity游戏开发】SpriteAtlas与AssetBundle最佳食用方案
一、简介
在Unity步入2019.4以后,新版的SpriteAtlas日趋完善,已经完全可以在商业项目中使用了。但是纵观网络平台上,许多关于SpriteAtlas的文章还停留在2018的初版时期,其中许多解释在现在看来都是过时的,甚至近期UWA问答上的一篇Q&A也是错误的结论,传送门。(笔者文章写于2020.9月)如果还按照CSDN或者UWA上的这种错误的教程来使用SpriteAtlas的话,一来有可能造成图集和资源的冗余,二来会导致享受不到新版图集带来的开发便利从而影响了效率。因此进行SpriteAtlas和AssetBundle的正确配合使用调研实在必行。
二、图集的往事今生
1.NGUI和TP时代
早在NGUI时代就已经有了图集的概念了,与UGUI先使用后制作图集的工作流程不同,NGUI是先制作图集再使用。
先制作图集再使用的时候,在反复迭代开发的过程中,图集打包容易引起冲突。同时,实现规划的图集随着需求的变更可能需要重新规划,重新规划以后,又要重新打图集,之前做好的界面又要重新修复引用,这中间的工作量,经历过的人都知道。
对于NGUI这种对迭代开发十分不友好的工作流,UGUI带来的改进可以说是广大开发者的福音。
在访问NGUI图集的图元时,我们需要先加载图集,然后再从这个图集中获取单个图元,用伪代码表示大概是这个流程:
var spriteAtlas = Resources.Load("spriteAtlasName"); var sprite = spriteAtlas:GetSprite("spriteName");
2.UGUI时代的旧SpritePacker和新SpriteAtlas
和NGUI不同,UGUI访问图集的图元不需要我们主动去加载图集,再从图集中获取图元。当我们加载图集的图元时,图集会被引擎自动加载,图集的释放也是自动完成的,不需要针对图集编写任何的业务逻辑代码,而有关图集的处理工作都是放在了资源后处理、资源分组和打包上。
简单来说,就是运行时写的那些业务逻辑不需要关心我这个图元属于哪一张图集,属于哪一个AssetBundle,直接以散图的形式去使用、去获取就可以了,比如代码可以写成下面这样:
var sprite = Resources.Load("Bag/spriteName");
UGUI图集的先使用后制作主要有两种作业模式,也就是旧的SpritePacker和新的SpriteAtlas模式:
1.旧的SpritePacker模式,给Sprite设置PackingTag,Tag相同的会被分配到同一个图集中,如下图所示:

2.新版的SpriteAtlas相比之前的SpritePacker做了更多的优化,比如可以实时查看图集的大小,图集里面元素的排列布局,并且增加了LateBinding等特性
我们可以通过Asset/Create/Sprite Atlas去创建一个图集,图集创建以后可以通过拖拽的方式去选择要打包的对象,如下图所示:

Objects for Pakcing指定了图集中需要打包的图元,可以指定单独的文件,也可以指定整个文件夹。
在新的作业模式中,每一个图集都是用一个单独的SpriteAtlas管理起来,图集的格式也被定义在这个资源中,预览单张图集不用像旧版的那样需要把所有的图集都集中在一个SpritePacker的编辑器窗口中预览,而是可以实时在Inspector窗口可视化预览。旧版的图集管理方式在图集数量多的时候,查找不方便还非常卡,新版的作业方式是一种分而治之的理念,更为方便和快捷。
需要注意的,UGUI的图集,无论新旧,在构建AssetBundle的时候,同一个图集内的所有图元都要放在同一个AssetBundle中,否则,如果同一个图集的图元被分散到多个AssetBundle中,那么每一个AssetBundle都会包含一份这个图集的Copy,最终的结果就是包体冗余、内存膨胀和加载耗时等问题。
如果是看的CSDN等网络上的教程的话,多半会让你不要勾选SpriteAtlas的Include In Build选项,说是会造成图集的双份冗余。但是这种说法实际上早就过时了,这个Bug早已经在Unity2018.4.6中修复了,所以我们在使用中放心大胆地勾选Include In Build就好了,这样也可以避免使用LateBinding。


同样,如果是看了网上的教程的话,也会发现有一些在使用SpriteAtlas时遇到了白图或者不显示的情况,这种情况实际上是对UGUI新图集的工作流不熟悉导致的。经过测试,只要打包的时候勾选图集的Include In Build,然后,不需要主动对SpriteAtlas资产文件进行打包,也不需要写额外的代码,就可以正常使用了,只需要对文件下下的散图进行ab打包即可。实例代码如下:
string path = "UI/Atlas/MySprite.png" var sprite = Assets.LoadAsset(path,typeof(Sprite)); //封装好的加载接口 MyImage.image = sprite;
三、实践工程
为了佐证上面的一些结论,这里特意配置了一份教练工程。
首先有一个引用了图集中图元的UIPrefab,如下图所示,它上面有三个Image,右边的两张Image分别引用了同一个图集中的图元,我们用它来验证图元是否合批,左边的Image是引用了另外一个图集中的图元,我们用它来对比验证DrawCall:


然后这里面有三分SpriteAtlas文件,它们都勾选了Include In Build 但是不参与打包。这三个图集分别管理这Sprites目录下的每个子目录中的散图文件,这些散图文件时需要参与打包的(AssetBundle)。

然后我们进行打ab操作,打出ab以后我们用AssetStudio去验证ab包的内容,看看它有没有冗余情况出现。首先看一下打出来的UIPrefab的ab文件,prefabs_uiroot.bundle,可以看到尽管UIPrefab引用了图集里面的图元,并且图集勾选了Include In Build,但是并没有UIPrefab的ab里面并没有冗余,Unity的确是修复好了这个bug:


然后再来看一下图集的ab包,因为没有对.spriteatlas文件特意的包,所以打包的实际上按目录划分的散图文件,shared_ui_sprites_game.bundle,可以看到里面只有Texture和Sprite,也没有多余的冗余文件出来:

但是,如果我们故意指定对.spritealtas文件也打包,特意指定一下,然后我们再看spriteatlas资源文件打出来的ab,atlas_game.bundle,可以发现里面有个合并好的512x512的图集,这个就造成了的冗余,因为散图目录下已经有一份资源文件了:

然后我们再看下,只设置了图集,但是不特意针对图集进行导出打包的情况下,UI使用图集内的图元,是否还可以正常的合批,DrawCall是否正常。
四、总结
实际上,通过上面的一系列测试,我们可以得出以下结论,新版的SpriteAtlas可以看做是对旧版的SpritePacker的升级,我们在使用的时候仍然是不需要关注图集这个东西的,这里的SpriteAtlas可以看做仅仅是用来作为一种对散图的归纳与整理。当我们加载图集的图元时,图集会被引擎自动加载,图集的释放也是自动完成的,不需要针对图集编写任何的业务逻辑代码,而有关图集的处理工作都是放在了资源后处理、资源分组和打包上。简单来说,就是运行时写的那些业务逻辑不需要关心我这个图元属于哪一张图集,属于哪一个AssetBundle,直接以散图的形式去使用、去获取就可以了。
简单来说遵循以下几点就不会有错了:
- 工作过程中(拼接UI等)放心大胆地以散图的方式去引用UI/Atlas/XXX下的各种图片即可
- SpriteAltas文件需要勾选Include In Build,但是不要特意打包,会造成冗余和包体膨胀
- 代码中动态加载Sprite的地方,直接使用散图的资源路径去加载就可以了,比如:var sprite = Assets.LoadAsset<Sprite>(path);
- 平时工作的机器上SpritePacker的Mode可以设置为Disable,这样可以提高效率(开启的话每次Run之前,图集会打包,资源多了以后会卡)。但是在打包机上一定要把Mode设置为Enable for build或者Always enable,这样图元才能被正确地合批
- 同一个图集内的所有图元打包时都要放在同一个AssetBundle中
如果觉得本篇博客对您有帮助,可以扫码小小地鼓励下马三,马三会写出更多的好文章,支持微信和支付宝哟!
作者:马三小伙儿
出处:https://www.cnblogs.com/msxh/p/14194756.html
请尊重别人的劳动成果,让分享成为一种美德,欢迎转载。另外,文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!
【Unity游戏开发】SpriteAtlas与AssetBundle最佳食用方案的更多相关文章
- C# Unity游戏开发——Excel中的数据是如何到游戏中的 (四)2018.4.3更新
本帖是延续的:C# Unity游戏开发--Excel中的数据是如何到游戏中的 (三) 最近项目不算太忙,终于有时间更新博客了.关于数据处理这个主题前面的(一)(二)(三)基本上算是一个完整的静态数据处 ...
- 喵的Unity游戏开发之路 - 玩家控制下的球的滑动
- 喵的Unity游戏开发之路 - 推球:游戏中的物理
很多童鞋没有系统的Unity3D游戏开发基础,也不知道从何开始学.为此我们精选了一套国外优秀的Unity3D游戏开发教程,翻译整理后放送给大家,教您从零开始一步一步掌握Unity3D游戏开发. 本文不 ...
- 关于Unity游戏开发方向找工作方面的一些个人看法
这是个老生常谈,却又是谁绕不过去的话题,而对于每个人来说,所遇到的情况又不尽相同,别人的求职方式和路线不一定适合你,即使是背景很相似的两个人,有时候机遇也很重要. 我本人的工作经验只有一年,就业方式 ...
- C# Unity游戏开发——Excel中的数据是如何到游戏中的 (二)
本帖是延续的:C# Unity游戏开发——Excel中的数据是如何到游戏中的 (一) 上个帖子主要是讲了如何读取Excel,本帖主要是讲述读取的Excel数据是如何序列化成二进制的,考虑到现在在手游中 ...
- C# Unity游戏开发——Excel中的数据是如何到游戏中的 (三)
本帖是延续的:C# Unity游戏开发——Excel中的数据是如何到游戏中的 (二) 前几天有点事情所以没有继续更新,今天我们接着说.上个帖子中我们看到已经把Excel数据生成了.bin的文件,不过其 ...
- 2017年Unity游戏开发视频教程(入门到精通)
本文是我发布的一个Unity游戏开发的学习目录,以后我会持续发布一系列的游戏开发教程,都会更新在这个页面上,适合人群有下面的几种: 想要做独立游戏的人 想要找游戏开发相关工作的人 对游戏开发感兴趣的人 ...
- 【Unity游戏开发】浅谈Lua和C#中的闭包
一.前言 目前在Unity游戏开发中,比较流行的两种语言就是Lua和C#.通常的做法是:C#做些核心的功能和接口供Lua调用,Lua主要做些UI模块和一些业务逻辑.这样既能在保持一定的游戏运行效率的同 ...
- Re:Unity游戏开发有哪些让你拍案叫绝的技巧?
这是我在知乎一个问题: <Unity游戏开发有哪些让你拍案叫绝的技巧?> 下面的回答,觉得蛮有趣的,贴在这里和博客的朋友们分享下. ----- 分享一个比较好玩的内容吧. 大家都知道Uni ...
随机推荐
- C#中的WinForm问题——使用滚动条时页面闪烁及重影问题
当使用鼠标进行滚动查看页面时,由于页面会频繁刷新,如果页面中控件较多会导致页面出现闪烁.重影等问题,如下图所示: 在网上搜索过该问题,大部分都说使用双缓冲可以解决此类问题,即通过设置DoubleBuf ...
- 04_ Broadcast Receiver
Broadcast是广播,和Android内的事件一样,它可以发出一个广播(事件),注册了该广播接收器(事件监听器)的所有组件都会接收到该广播,从而调用自己的响应方法(事件响应处理). 下面将详细的阐 ...
- IdentityServer4系列 | 授权码模式
一.前言 在上一篇关于简化模式中,通过客户端以浏览器的形式请求IdentityServer服务获取访问令牌,从而请求获取受保护的资源,但由于token携带在url中,安全性方面不能保证.因此,我们可以 ...
- 【GDKOI2014】JZOJ2020年8月13日提高组T3 壕壕的寒假作业
[GDKOI2014]JZOJ2020年8月13日提高组T3 壕壕的寒假作业 题目 Description Input Output 输出n行.第i行输出两个整数,分别表示第i份作业最早完成的时刻以及 ...
- PyQt(Python+Qt)学习随笔:QDateEdit日期编辑部件和QTimeEdit时间编辑部件
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 Designer输入部件中,Date Edit和T ...
- Python中sorted(iterable, /, *, key=None, reverse=False)的参数中的斜杆是什么意思?
通过help(sorted)查看sorted的帮助文档,显示如下: Help on built-in function sorted in module builtins: sorted(iterab ...
- PyQt(Python+Qt)学习随笔:视图中的拖放操作注意事项
老猿Python博文目录 老猿Python博客地址 在通过PyQt构建的图形界面中进行拖放,要成功进行拖放需要注意: 视图相关属性需要支持拖放,具体相关属性请参考<PyQt(Python+Qt) ...
- python中的Restful
哇,昨天组里进行总结的时候,小哥哥和小姐姐真是把我给秀到了,跟他们一比,我总结的太垃圾了,嘤嘤嘤.因为我平常不怎么总结,总结的话,有word还有纸质的,现在偏向于纸质,因为可以练练字.个人观点是,掌握 ...
- 记阿里云 RDS MySQL 的一个大坑
花了一个下午的时间,终于把一个阿里云 RDS MySQL 的一个大坑填上了,解决方法令人匪夷所思!绝对会让各位看官感到大吃一惊,阿里云 RDS MySQL 居然有这样 xx 的大坑! 问题 最近应业务 ...
- Java并发编程的艺术(十一)——Executor与线程池
Executor框架简介 从JDK5开始,把工作单元和执行机制分离开来了,工作的单元包括Runnable和Callable,执行机制就是由Executor框架提供. Executor两级调度模型 Ho ...