最糟糕的是人们在生活中常常受到错误志向的阻碍而不自知,真到摆脱了那些阻碍时才能明白过来。 —— 歌德
说到UGUI的图集初学者可能以为没什么难度,包括我刚开始接触的时候也是,甚至你在开发的时候只须要把图片导入到项目中,拖拖拽拽就能作出能用的东西来。由于UGUI刚出的时候就打出了“Unity会自动帮你维护图集”的旗号。可现实真的是这样的吗?要解释这个问题就须要从Unity4.6提及了,那咱们来捋一下!程序员
这功能是从Unity4.6版本随着UGUI的问世一块儿发布的,咱们在作开发的时候只须要把图片导入工程,设置一下,而后在经过tag标签Unity就会自动打成图集。可是,这种方法有个问题,就是咱们在运行时没法经过代码取出某张图集中的一张小图。算法
因此后来开发者们作了一套prefab引用sprite的解决方案,作法就是:建一个空prefab挂一个自定义脚本,脚本里有一个sprite数组,咱们在编辑时把要打的图集中的sprite添加到这个sprite数组中,而后在把prefab打成bundle,这样这个bundle中的脚本就会有atlas中的Sprite的引用,咱们在脚本里写一个GetSprite方法便可。这样咱们在运行时加载完bundle后就能够经过脚本取出atlas里的sprite。数组
using System.Collections.Generic; using UnityEngine; public class Test : MonoBehaviour { public List<Sprite> sprites; public Sprite GetSprite(string spriteName) { return sprites.Find(s => s.name.Equals(spriteName)); } }
可是,讲真,就这么个函数Unity提供一个API很难嘛!这个问题致使从Unity4.6到Unity2017.1每次作图集动态加载的时候都要用上面的方法搞一遍。固然要是仅仅是这点工做量也就忍了,重要的是这么作还会影响到打包、依赖的管理。打包依赖又是个Unity的大坑。。。一个坑跳到了另外一个更深的坑。。。固然也是有解决办法的,打包依赖问题咱们之后再专门讲,这里就很少说了。编辑器
SpritePacker的TightPackerPolicy(紧凑),根本就不能用!SpritePacker打图集时有两个选项:默认、紧凑,然而使用过你会知道其实这个“紧凑”选项是不能用的!!打出来的图会串,你用一张Image指定了一张Sprite A,运行时你会发现若是用“紧凑”打出来的图集会把A的范围算错,表现就是显示的图片不是A,可能只包括A的一部分,其余部分会显示别的图片,其实就是取A这张图片的时候位置没算对!!!最终你会发现也只能使用”默认”方式打图集了。若是想优化图集的空间利用率,只有一个办法就是本身写打图集的排列算法,固然Unity也提供了这个接口!函数
SpriteAtlas是2017.1版本之后更新的一个新功能,它能够把图片“手动”打成一张图集。若是你以为这是Unity的一个新功能或者一个强化图集的功能那你就错了,若是仅仅是打张图集也不必单独搞这么个玩意儿,其实这也是Unity在“还SpritePacker的账”,固然咱们上面也讲到了,能够确定的一点是SpritePacker在商业项目中:是能用的!可是不可避免的你会为了填补SpritePacker的一些坑。优化
SpritePacker时代“图集”咱们是看不到的,由于设计者的初衷是想让开发者彻底不用考虑图集的事。可是,这跟游戏开发时的动态加载图集时矛盾的!咱们要动态加载就必需要知道atlas名字和sprite名,这样才能在运行时动态的找到一张sprite!因此U3D程序员必需要清楚的知道你当前界面用的是哪atlas的哪一个sprite。可是按照SpritePacker的作法他的初衷应该是想把图集干掉(干掉的意思让开发者不用关心),只须要考虑sprite便可,可是这是行不通的!ui
SpriteAtlas其实就是为了解决上面说的问题而发布的功能。经过它咱们能够将atlas和UIprefab“解耦”。同时,在Unity编辑器状态下咱们仍然能够利用未打成SpriteAtlas的Sprite进行开发。等开发完成咱们再发布成SpriteAtlas。Unity提供了所谓“延迟绑定”(late bind)的技术来让咱们实现这个功能。具体作法以下:spa
在Unity2017.1以后,UI prefab的Bundle在被加载的时候会有一个回调函数被调用:SpriteAtlasManager.atlasRequested,这个委托的定义是这样的:void RequestLateBindingAtlas(string atlasName, System.Action output);设计
(代码不严格,当成伪码看就行))code
SpriteAtlasManager.atlasRequested += (atlasName, output) => { string path;//省略,atlas bundle加载路径 AssetBundle bundle;//省略,加载atlas的asset bundle var atlas = bundle.LoadAsset<SpriteAtlas>(path + "/" + atlasName); output(atlas); }
咱们须要在运行时把atlas绑定到当前的prefab中,这个绑定是经过第一个参数"atlasName"关联的。若是prefab中引用了多张atlas他会被调用屡次,每次都会把atlasName传过来,由“用户”决定加载哪张atlas。最后调用output(atlas)把图集传给prefab。
根据实测,这个选项的含义是:选,打包时不考虑依赖。不选,打包时考虑依赖。
举例:
文件夹"A"存放着要使用的图片,SpriteAtlas文件“A_Atlas”为文件A下全部图片打成的图集,Prefab文件“A_Prefab”引用了A下的图片。
此时,
若是选择Include in Build,那么打包的时A_Prefab打出来的bundle文件会包含A下面的图片,A_Atlas打出来的bundle也会包含A下面的图片,也就是说此时会打包双份资源。这是咱们不想看到的。
若是不选Include in Build,只有A_Atlas打出来的bundle会包含A下的图片,而A_Prefab不会包含A下的图片。这样资源只打包了一份,是咱们想要的结果。
可是,
有个地方须要注意就是,若是A_Atlas第一次勾选了Include in Build,打包后A_Prefab和A_Atlas的bundle都会包含图片资源,可是若是此时再把A_Atlas的Include in Build的勾去掉从新打包,Unity只会把A_Atlas的bundle重打,由于Unity认为你只修改了A_Atlas文件,而A_Prefab被认为”没有更改”,因此以前打进去的图片资源依然会存在,正确的作法应该是:若是Include in Build修改了,那么打包的时候全部用到这个SpriteAtlas的prefab也要从新打包(删掉bundle文件便可)
这应该是Unity的一个bug,在这里分享一下!
在咱们的实测中发现:在加载资源的时候若是使用延迟绑定的方式显示UI,在UnityEditor模式下UI的prefab加载显示后全部图片都是白图!可是发布到设备中能正确显示。
莫非延迟绑定只能运行时使用?
Texture Packer最终的解决方案,固然这也是很早之前就使用的方法了。若是Unity能把这件事作好,我相信你们都不想本身单独用第三方软件去搞图集。TexturePacker具体功能和使用方法就很少说了,网上相关教程有不少。
这里我只说一下TexturePacker解决了Unity的哪些问题:
1.图集打包算法,选择不少,也更专业。
2.动态加载图片,简单明了。编辑时加载:AssetDatabase.LoadAllAssetsAtPath()或者AssetDatabase.LoadAllAssetRepresentationsAtPath();运行时加载:AssetBundle.LoadAllAssets()
3.图集的依赖关系,很直观。
固然也有缺点:
须要维护TexturePacker工程文件。
总结一下:
SpritePacker,商业项目可用,须要本身作点东西“填坑”。
SpriteAtlas,商业项目可用,他解决了SpritePacker的小问题,可是本身有带来了新问题,若是“白图”的bug解决不了,可能须要单独写一套编辑时的加载流程。感受和SpritePacker填的坑差很少!
TexturePacker,商业项目能够,相对来讲比较“健康”,不用额外写代码填坑。
从本文还能够看出一点,Unity对UI的重视程度仍是比较小的!
就说到这里吧!Have a good day!