首页 > Unity3D频道 > 【UGUI研究院】 > UGUI研究院之全面理解图集与使用(三)
2014
10-24

UGUI研究院之全面理解图集与使用(三)

UGUI的图集打包与工作原理,整整看了一天多,终于看明白了~晕~还是记录一下我研究的成果,也希望大家在下面给我留言我们一起讨论一下。

先说说UGUI的Atlas和NGUI的Atlas的区别,NGUI是必须先打出图集然后才能开始做界面。这一点很烦,因为始终都要去考虑你的UI图集。比如图集会不会超1024 ,图集该如何来规划等等。而UGUI的原理则是,让开发者彻底模糊图集的概念,让开发者不要去关心自己的图集。做界面的时候只用小图,而在最终打包的时候unity才会把你的小图和并在一张大的图集里面。然而这一切一切都是自动完成的,开发者不需要去care它。

如下图所示,Editor->Project Settings 下面有sprite packer的模式。Disabled表示不启用它,Enabled For Builds 表示只有打包的时候才会启用它,Always Enabled 表示永远启用它。 这里的启用它就表示是否将小图自动打成图集。

UGUI研究院之全面理解图集与使用(三) - 雨松MOMO程序研究院 - 1我的选项是Always Enabled 。因为开发的时候我们需要清楚的看到现在是几个Draw Call,从而才能优化小图。在最终打包的时候unity会自动构建大的图集,可是我开发的时候就想看图集会占几个Draw Call,这怎么办呢?如下图所示,首先将你的图片拖入unity中,将同一图集的所有图片的packing tag设置成一个名子即可。

UGUI研究院之全面理解图集与使用(三) - 雨松MOMO程序研究院 - 2

注意你的图片不能放在Resources文件夹下面,Resources文件夹下的资源将不会被打入图集,切记(也就是在这里混淆了我很久)。然后在Windows->Sprite Packer 里,点击packer 在这里你就可以预览到你的图集信息。图集的大小还有图集的格式等等很多参数我们都是可以控制的,也可以通过脚本来设置。我在下一篇文章里详细说这个(请期待嘿嘿)。

UGUI研究院之全面理解图集与使用(三) - 雨松MOMO程序研究院 - 3

图集的预览紧紧是让你看看你的图集大概张什么样子。那么我们的图集的这张图片保存在了哪里呢?它保存在和Assets文件夹同级的目录,Libary/AtlasCache里面。你不用管它,也不要删除它,就算你删除了也没用因为只要你打包,它就会生成并且会打到包中。

此时在Hierarchy视图中创建两个Image对象。如下图所示,我们可以清楚的看到此时我的draw call已经被合并成了1 。

UGUI研究院之全面理解图集与使用(三) - 雨松MOMO程序研究院 - 4

这两个图片是我是在Editor模式下预先拖入Hierarchy视图中的,可是如果我想运行时根据图片的名子来动态创建精灵该如何?可是unity根本没有提供加载图集的方法,也没有提供加载图集上某个图片的方法。 因为UGUI就不像让开发者有图集的这个概念,可是我们肯定是要实现这个需求的。。怎么办呢?

第一个设想,先把散=小图打包成图集,然后再把所有散图拷贝在Resources文件夹下,这样运行时就能用Resources.load了。

第二个设想,还是先把小图打成图集,然后把所有小图关联在prefab上,拷贝在Resources文件夹下,这样运行时也能用Resources.load了。到底那个靠谱呢? 给大家看一个图大家就知道答案了。

如下图所示,打成图集的图片如果在放在Resources那么资源就变成双份了。。 所以我们只能把小图关联在Prefab上,把所有的Prefab放在Resources下面,这样就不占用多余的空间了。

UGUI研究院之全面理解图集与使用(三) - 雨松MOMO程序研究院 - 5

好了,现在方法我们已经掌握,那么就开始写工具吧。如下图所示可以按文件夹分,每一个文件夹就是一个图集。然后每一张小图创建一个Prefab,Prefab的名子就起小图的名子,文件关联在Resources下面。

UGUI研究院之全面理解图集与使用(三) - 雨松MOMO程序研究院 - 6

 

代码比较简单,我就不注释了。

 

然后是运行时的代码。

因为这两个图是在同一个图集上,所以drawcall就是1了。这样我们就可以根据图片的名子来运行时加载图片了。

UGUI研究院之全面理解图集与使用(三) - 雨松MOMO程序研究院 - 7

接下来就是Assetbundle了,如果我们的图集需要在线更新那该怎么办呢? 其实Assetbundle比Resources要更简单一些,无论如何我们要先开始打图集。

如下图所示,我的assetbundle已经打出来了。

UGUI研究院之全面理解图集与使用(三) - 雨松MOMO程序研究院 - 8

然后把UIMain.cs在改一改。

如下图所示,依然还是一个drawcall。

UGUI研究院之全面理解图集与使用(三) - 雨松MOMO程序研究院 - 9

 

衷心希望有经验的朋友在留言处给我提提意见, 或者大家一起讨论讨论。。 我们共同为把NGUI干掉的目标而奋斗,嘻嘻。

 

雨松MOMO提醒您:亲,如果您觉得本文不错,快快将这篇文章分享出去吧 。另外请点击网站顶部彩色广告或者捐赠支持本站发展,谢谢!

--

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠如果您愿意花10块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。

UGUI研究院之全面理解图集与使用(三)》有 177 条评论

  1. 见林 说:

    雨松,能不能提供下源码,这个sprite打包的,一直没成功:(

  2. 雅伦 说:

    雨松你好,请教一个问题,我将多个sprite打成图集,并打成Assetbundle。然后客户端通过www.loadFromCacheOrDownload下载图集。如果我一个uiimage想要换图的话,继续使用loadFromCacheOrDownload,可以实现功能,但这是异步的。当我加载多个图片的时候会逐个进行显示,表现效果很奇怪。CreateFromFile或者LoadAssetFromFile 都需要知道明确的文件地址,loadFromCache的文件路径地址,有没有什么好的解决方法

  3. john 说:

    雨松请教一个材质贴图打图集,打AB后,加载出来贴图丢失的问题:我把材质贴图打成了图集,并打成了AB。接着Material引用这些贴图,Material打成AB,新建一个cube的立方体,材质用刚才的Material,cube打成AB。这时我有3个AB,一个是贴图的AB,Material的AB,cube的AB,以及一个材质贴图的图集。然后分别加载这三个AB,再从cube的AB中取出cube并实例化,结果发现材质cube上的贴图丢失。反复试验后,发现如果不把材质贴图打成图集,就不会丢失。请问这时怎么回事? 难道unity不支持这样?感觉应该至于吧,同一个npc的材质贴图应该是要打到一个图集中用于降低CD的呀。难道用了AB就不行啦?

    • john 说:

      自己解决了。必须把图片的Sprite mode:Multiple,如果选single的话,就会出现从AB加载Material丢失贴图。但是如果不打图集就没事。还是有疑问,发现能打图集的图片,只能是类型是:Sprite (2D and UI),其他类型不支持。不知道雨松用的什么方案?

  4. john 说:

    非常感谢雨松的方案:1.我用的5.3.4f1的版本,实验了下你介绍的方案,得出的实验结果和有稍微有点不同。如果不打图集,只是多张图打到一个AB中,然后从这个AB中读出多张小图加载到UI上,这时始终只消耗1个calldraw。也就是说用AB,可以不打图集。其他版本没试过,不知道具体情况。2.我看你在给下面的朋友回复说,你最新的方案是把图集打成AB。请介绍下大致的流程。因为我理解如果打了图集,大图并不在Asset目录下。这个怎么做热更新?以及差异更新包?

    • Dean 说:

      理论上来说如果这样的话,首先你创建了一个go,引用了AB里面的一个资源a.png,然后当你清理这个AB并且执行了Resources.UnloadUnusedAssets()以后,别的地方再引用了这个AB的别资源b.png图,这时候应噶就会产生重复资源了。那这时候calldraw原本是1的就会变成2了吧?

      • john 说:

        修正下,5.3.4f1最新得出的结果与雨松的一致。也就是既打图集又打AB. 之前为什么出现只打AB,而不打图集就能降低CD的原因是在于我最开始是打了图集的,如果在没有关闭工程的情况下,再取消打图集,打AB,这样CD能降低,但重新打开工程就不能降低CD了。不知道这算不算unity的一个BUG.

  5. 阿举 说:

    雨松大神,还有个问题请指导一下:我现在是一个UI使用自己的图集,但是按钮这些通用的,所有UI使用一个Common图集。打bundle时,只打UI.prefab的bundle,这时候它会自动使用自己的图集没问题,但是因为“每个UI”都会带上通用的图,导致bundle变大,并且这样貌似每个UI加载都会再通用图集Common的图集。我的解决是:1.把这个Common图集打成一个bundle,但是这样打出来的UI还是原样大小,并不起啥使用。 (这个common.bundle和UI并无依赖,我白加了貌似)2.为了让他们有依赖,然后我用你的方式,一个Common图片打一个bundle,发现每个图片的bundle都得1M多,并且ui.bundle也是原样大小,也无改善。3.再想着,如果我把所有图片像你这样关联到一个pic.prefab,再将它打bundle,运行时先加载它和它上面的图片,再加载ui.bundle,不知道能不能这样做,但如果就算这样,我的bundle貌似还是会关联么通用的图片,还是那么大,没办法解决这个问题。请momo指导一下思路。

    • 阿举 说:

      雨松,我测试发现个问题,就是如果只要我ui用了这个图集的一个图片,打出的ui.bundle我用bundle export解开看了下,它是只有这一个图片,但它的大小,是只要用了哪个图集,就把这个图集的大小全打进去了,如果用了a图集一张和b图集的一张,和用了a图集的n张和b图集的n张没有区别,会有其它的不用的无名称的sprite占位。不知道是不是和版本有关系,我是 5.0.1

  6. 阿举 说:

    雨松大神,有个问题请指导一下:我的资源是:Asserts/Texture/UINameTexture (这里放的是UI的图片,我每个设置了tag,相同的这一个UI是一个静态图集) Asserts/Resources/UIName (这里放的是UI的prefab,它的图是直接使用UINameTexture里的图直接拖上去的2dsprite) (UI是动态更新的)Asserts/Resources/UIDynamic (这里是我的动态资源,比如英雄头像,装备图标), 我用的时候是在UI脚本里2dSprite = Resources.Load()加载赋值的. (它们每种类型放进一个图集)我的策略:所有的UI是一个xml,查找最新的UI,然后只更新UI.prefab,如果UI只更新了一个图,那么也是彻底更新这个UI的assertbundle。我的问题:1.我现在动态更新的UIName.prefab->打成的uiname.assertbundle 这个文件很大(最大的有7M左右),请帮我看下我有什么可以优化的地方。2.为什么我打出来的bundle这么大,有什么不合理的地方吗?我应该再怎么组织资源?3.我最初的时候是把UIName的prefab放在Resources外面的,然后直接所有的UI都打成bundle使用StreamingAssserts里(1)这样打出来的apk很大,但打成的uiname.assertbundle只有500K很小,更新出完全可以。(2)但这样打出来的包很大,就改成现在放Resources目录,现在apk是小了,但是uiname.assertbundle大到不可以接受了。请雨松大神给点建议。

  7. 五识一念 说:

    大神您好,我想问您一下,我想做一个类似内存池的东西,可以根据内存大小来卸载多余的图集,但是我怎么能知道图集占用多少内存,或者图集的大小是多少?

  8. 郭爱民 说:

    图片序列化在一个prefab上关联脚本可以分享一下吗

  9. 袁晓林 说:

    大神想问一下,打好图集后,Drawcall是正常的,但是把拼好的图旋转以后Drawcall就不正常了,回到了没有打图集时候的Drawcall。

  10. 冬晓 说:

    雨松大神 请问你测试这个用的Unity哪个版本?

  11. 冬晓 说:

    还有一个问题:我使用Unity5.0.1将制作成图集后的Sprite打包成一个AssetBundle后包的大小反而增大了,比不制作成图集在打包成AssetBundle的包大了几倍,然后我改成Unity5.2.4后就好了,请问这是Unity5.0.1的bug吗?

  12. 冬晓 说:

    雨松 你好 请教您个问题:把一些UI预制体(没有Sprite)打包成了AssetBundle,然后将打包成图集后的sprite也打包成AssetBundle,最后将Sprite和UI预制体在AssetBundle加载出来,再将Sprite放到UI预制体上,运行后DrawCall并没有降低。请问该怎么解决?

  13. DAI 说:

    NGUI作者多良心,一直源码给你,而且更新频繁,正式有了它我们才没有在ui这一块浪费太多的时间,虽然现在原生的确实比NGUI用,但是不要说干掉NGUI嘛,心酸啊!

  14. TrustTJM 说:

    雨松大神,真心求教个问题,不知道是我测试用例的原因,老是没有得到正确的测试结果,所以特来求教,万望解惑呢

    是关于打Atlas的AssetBunlde问题,(用你这篇文章的demo工程目录结构):
    (1)假如我有一堆英雄头像作为一个图集Atals_HeroIcon,然后这些英雄头像是需要热更新的,比如说精灵A,因为某些特殊需求,在某个界面上一定只需要显示A,那么这时候假如我不用代码动态读取AssetBunlde中的A来放上去,而是直接把(目录:Atlas/../A.png)拖到对应的Image上。然后问题来了,这个时候,如果ipa或者apk是会包括Atals_HeroIcon这个大图吗?如果成立,可不可以反过来说,假如某个图集需要热更新或者全部都用代码来动态加载sprite,那么就可以节省包体大小?
    不过我测试了貌似没有什么效果咧,求指点。

    (2)假如有个图集Atals_Common,里面有些sprite需要代码动态Resources Load出来,有些只需要把小图给直接拖上去就好了,但这个图集不需要热更新,那么,其实是不用打AssetBunlde的是吧?只需把这个图集生成一份prefab,确保可以让某些sprite可以代码动态Resources Load出来,其他的该拖的拖?

    (3)假如我上面两点猜的没有错,那一般管理的处理流程是不是就为这样呢?热更新的图集全部用代码,保证没有任何关联的饮用;其他的图集都直接生成一份prefab,以供能动态用Resources给Load出来?

  15. TrustTJM 说:

    雨松大神,真心求教个问题,不知道是我测试用例的原因,老是没有得到正确的测试结果,所以特来求教,万望解惑呢

    是关于打Atlas的AssetBunlde问题,(用你这篇文章的demo工程目录结构):
    (1)假如我有一堆英雄头像作为一个图集Atals_HeroIcon,然后这些英雄头像是需要热更新的,比如说精灵A,因为某些特殊需求,在某个界面上一定只需要显示A,那么这时候假如我不用代码动态读取AssetBunlde中的A来放上去,而是直接把(目录:Atlas/../A.png)拖到对应的Image上。然后问题来了,这个时候,如果ipa或者apk是会包括Atals_HeroIcon这个大图吗?如果成立,可不可以反过来说,假如某个图集需要热更新或者全部都用代码来动态加载sprite,那么就可以节省包体大小?
    不过我测试了貌似没有什么效果咧,求指点。

    (2)假如有个图集Atals_Common,里面有些sprite需要代码动态Resources Load出来,有些只需要把小图给直接拖上去就好了,但这个图集不需要热更新,那么,其实是不用打AssetBunlde的是吧?只需把这个图集生成一份prefab,确保可以让某些sprite可以代码动态Resources Load出来,其他的该拖的拖?

    (3)假如我上面两点猜的没有错,那一般管理的处理流程是不是就为这样呢?热更新的图集全部用代码,保证没有任何关联的饮用;其他的图集都直接生成一份prefab,以供能动态用Resources给Load出来?

    • 雨松MOMO 说:

      你说的是正确的, 我说说我现在的方法把。 每个图集做成assetbundle. 然后每个界面的布局prefab也做成assetbundle. 来进行依赖图集。打开界面实际上就是 从assetbundle里把ui的prefab实例化进来。至于prefab上一些比如头像这种用代码根据名字动态创建。

      • 梦骑士 说:

        打包成图集比原包大了不少,请问大图集应如何压缩呢

        • 阿举 说:

          应该是你的UI使用不同图集的原因,我也有这个问题,本来UI图片总只有100k,因为使用的公共图片的2M,导致我的uiprefab.assertbundle也会变成2M多(如果依赖图片不在Resources里,它公共一依赖的东西在不同UI里就是多份)感觉:1.把公共依赖的图再单独打成一个bundle,UI使用全bundle的方式加载 (如果预加载公共bundle的话,之后的UI加载会快好多)2.共同图放到resources,不能更新,但会减少UI打出bundle的大小。借momo宝地继续实践完回复。

  16. zhongdejun666 说:

    你好啊,请问一下按照第二种做法,感觉好像只能在代码中对Image设置Sprite,那么在编辑界面中对Image设置的Sprite还管用?

  17. 阿尼薇__ 说:

    哈喽,雨松大大,问你个问题吗,当sprite设置了tag,表示他属于某个图集,在设置assetbundle和不设置的时候,我发现对应的预置键的大小都是一样的,只是存在有没有依赖关系,感觉像是预置键里自动拷贝了一份sprite。如果设置assetbundle会增加包大小,不设置,那么在做热更新的时候又不能直接更新sprite。。而且很好奇的是,既然设置了tag,那么系统应该自动打成一张大图吧?那那张大图在打包assetbundle又在哪里呢?

  18. 王振丰 说:

    感觉Sprite Packer优点在于不用图集,但是根据imageName加载还是需要做图集,那是不是说动态加载还是用NGUI好啊

  19. 杨云 说:

    你说的第二种加载你想要的图片的方法不靠谱吧,图片量一大,成千上万个prefab,相较NGUI图集这样的方法太粗糙了。而且可能内存方面也比较大了。有更好的方法吗momo

  20. 刘义杰 说:

    大大,有问题想问你。菜鸟一枚。
    想问UGUI 怎么实现多张图片滑动。加了个Mask和ScrollRect。总是滑倒两张图片中间。想要一个当滑倒两张图片缝隙时候,多的那张自动在中心

  21. 蛋疼行者 说:

    你好,雨松大哥,我是你的读者,最近在用到ugui时遇到一点疑惑,还请指教一二,先行谢过了!
    1,用ugui开发项目,关于图片格式,ui元素图片和动态加载的物品图片格式需要区分对待吗,我目前都是用的Sprite格式;
    2,由于我的动态图片采用的是Sprite格式,又想压缩资源大小,于是出现了如下问题,复现步骤如下:
    在pc平台模式下,我在对图片资源进行配置时,选择类型为Sprite,然后用image组件正常显示在舞台上,但资源格式是raga32的,为了节约资源,我进入Advanced设置,选择dxt5–apply,提示必须是2的幂次方,我就设置了non power of 2,发现图片马上显示不了了,为白色,后来多次测试,只要设置了non power of 2,都显示不出来,不论什么格式;

    附图如下:
    UGUI研究院之全面理解图集与使用(三) - 雨松MOMO程序研究院 - 1

  22. 雨松大大 有个问题 就是 直接用你最后一段代码 打包成AB,在打包的时候 是unity 自己把几个小图打成图集然后再打包吗? 还是直接把小图打成AB ,因为代码里面没有打包成图集的代码我很疑惑

  23. 程序狗 说:

    打包后,return assetbundle.Load(spriteName) as Sprite;中跟踪发现为null,怎么解决

  24. 那些年 说:

    我这边的 image 根本显示不到应有的sprite,单纯把创建好的2DSprite 丢进去, 是可以看到的

留下一个回复

你的email不会被公开。