一、简介

  在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设置PackingTagTag相同的会被分配到同一个图集中,如下图所示

2.新版的SpriteAtlas相比之前的SpritePacker做了更多的优化,比如可以实时查看图集的大小,图集里面元素的排列布局,并且增加了LateBinding等特性

  我们可以通过Asset/Create/Sprite Atlas去创建一个图集,图集创建以后可以通过拖拽的方式去选择要打包的对象,如下图所示:

  Objects for Pakcing指定了图集中需要打包的图元,可以指定单独的文件,也可以指定整个文件夹。

  在新的作业模式中,每一个图集都是用一个单独的SpriteAtlas管理起来,图集的格式也被定义在这个资源中,预览单张图集不用像旧版的那样需要把所有的图集都集中在一个SpritePacker的编辑器窗口中预览,而是可以实时在Inspector窗口可视化预览。旧版的图集管理方式在图集数量多的时候,查找不方便还非常卡,新版的作业方式是一种分而治之的理念,更为方便和快捷。

  需要注意的,UGUI的图集,无论新旧,在构建AssetBundle的时候,同一个图集内的所有图元都要放在同一个AssetBundle中,否则,如果同一个图集的图元被分散到多个AssetBundle中,那么每一个AssetBundle都会包含一份这个图集的Copy,最终的结果就是包体冗余、内存膨胀和加载耗时等问题。

  如果是看的CSDN等网络上的教程的话,多半会让你不要勾选SpriteAtlasInclude 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是否正常。

  通过上面的一些信息,我们可以看到Unity进行了合批操作,DrawCall为3也是正常的。在FrameDebugger里面也可以看到引用了同一图集内的两张图元的Image,也是在一个Batch里面去绘制的。

四、总结

  实际上,通过上面的一系列测试,我们可以得出以下结论,新版的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最佳食用方案的更多相关文章

  1. C# Unity游戏开发——Excel中的数据是如何到游戏中的 (四)2018.4.3更新

    本帖是延续的:C# Unity游戏开发--Excel中的数据是如何到游戏中的 (三) 最近项目不算太忙,终于有时间更新博客了.关于数据处理这个主题前面的(一)(二)(三)基本上算是一个完整的静态数据处 ...

  2. 喵的Unity游戏开发之路 - 玩家控制下的球的滑动

  3. 喵的Unity游戏开发之路 - 推球:游戏中的物理

    很多童鞋没有系统的Unity3D游戏开发基础,也不知道从何开始学.为此我们精选了一套国外优秀的Unity3D游戏开发教程,翻译整理后放送给大家,教您从零开始一步一步掌握Unity3D游戏开发. 本文不 ...

  4. 关于Unity游戏开发方向找工作方面的一些个人看法

     这是个老生常谈,却又是谁绕不过去的话题,而对于每个人来说,所遇到的情况又不尽相同,别人的求职方式和路线不一定适合你,即使是背景很相似的两个人,有时候机遇也很重要. 我本人的工作经验只有一年,就业方式 ...

  5. C# Unity游戏开发——Excel中的数据是如何到游戏中的 (二)

    本帖是延续的:C# Unity游戏开发——Excel中的数据是如何到游戏中的 (一) 上个帖子主要是讲了如何读取Excel,本帖主要是讲述读取的Excel数据是如何序列化成二进制的,考虑到现在在手游中 ...

  6. C# Unity游戏开发——Excel中的数据是如何到游戏中的 (三)

    本帖是延续的:C# Unity游戏开发——Excel中的数据是如何到游戏中的 (二) 前几天有点事情所以没有继续更新,今天我们接着说.上个帖子中我们看到已经把Excel数据生成了.bin的文件,不过其 ...

  7. 2017年Unity游戏开发视频教程(入门到精通)

    本文是我发布的一个Unity游戏开发的学习目录,以后我会持续发布一系列的游戏开发教程,都会更新在这个页面上,适合人群有下面的几种: 想要做独立游戏的人 想要找游戏开发相关工作的人 对游戏开发感兴趣的人 ...

  8. 【Unity游戏开发】浅谈Lua和C#中的闭包

    一.前言 目前在Unity游戏开发中,比较流行的两种语言就是Lua和C#.通常的做法是:C#做些核心的功能和接口供Lua调用,Lua主要做些UI模块和一些业务逻辑.这样既能在保持一定的游戏运行效率的同 ...

  9. Re:Unity游戏开发有哪些让你拍案叫绝的技巧?

    这是我在知乎一个问题: <Unity游戏开发有哪些让你拍案叫绝的技巧?> 下面的回答,觉得蛮有趣的,贴在这里和博客的朋友们分享下. ----- 分享一个比较好玩的内容吧. 大家都知道Uni ...

随机推荐

  1. python中字符串的编码和解码

    1. 常用的编码 ASCII:只能表示一些字母,数字和特殊的字符,占一个字节 GBK:国家简体中文字符集和繁体字符集,兼容ASCII,占两个字节 Unicode:能够表示全世界上所有的字符,Unico ...

  2. CSS色调旋转滤镜

    一 关于filter 首先看一下官方对于CSS的filter属性的定义: CSS属性将模糊或颜色偏移等图形效果应用于元素.滤镜通常用于调整图像,背景和边框的渲染. 本文主要讲的是filter中的一个属 ...

  3. gitlab 文件读取+rce复现 CVE202010977

    文件读取实现,首先生成两个project 再任意一个project添加issue,然后描述如下: ![a](/uploads/11111111111111111111111111111111/../. ...

  4. Alpha冲刺-第八次冲刺笔记

    Alpha冲刺-冲刺笔记 这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzzcxy/2018SE2 这个作业要求在哪里 https://edu.cnblogs. ...

  5. Android Studio下的简单网页解析

    Android Studio下的简单网页解析 一.导入数据 导入前添加依赖 implementation 'org.jsoup:jsoup:1.11.3' 使用字符串导入 String html = ...

  6. java并发编程实战《五》死锁

    一不小心就死锁了,怎么办? 在上一篇文章中,我们用 Account.class 作为互斥锁,来解决银行业务里面的转账问题,虽然这个方案不存在并发问题,但是所有账户的转账操作都是串行的,性能太差. 向现 ...

  7. PyQt(Python+Qt)学习随笔:Qt Designer中主窗口对象的tabShape属性

    tabShape属性用于控制主窗口标签部件(Tab Widget)中的标签的形状,对应类型为QTabWidget.TabShape,有两种取值: 1.QTabWidget.Rounded:对应值为0, ...

  8. 【Docker】 CentOS7 安装 Docker 及其使用方法 ( 一 )

    系列目录: [Docker] CentOS7 安装 Docker 及其使用方法 ( 一 ) [Docker] 使用Docker 在阿里云 Centos7 部署 MySQL 和 Redis (二) [D ...

  9. Oracle函数使用1

    一.字符串处理函数 1.ascii(x):返回字符的ASCII. SQL语句:select ascii('a') from dual; dual:空表,每创建一个用户都会生成这样一个dual表,表中只 ...

  10. java试用静态图片制作gif

    参考博客:https://www.cnblogs.com/dreammyle/p/4843365.html 代码中需要的依赖: <!-- gif --> <dependency> ...