iOS下拆分Unity图集的透明通道(不用TP)
- 作者:admin
- /
- 时间:2018年02月28日
- /
- 浏览:7825 次
- /
- 分类:厚积薄发
在写这篇文章之前,我先给大家展示下,下图是iOS目前PVRTC对透明图的压缩效果。
有人说是Unity没有用“正版”的PVRTC压缩库的原因,但是我专门去下载了独立版的PVRTexTool,对比图如下,大家觉得哪个是独立版的效果呢?是不是感觉差异并不明显?
实际上,Unity在安卓下已经自带图集ETC1+Alpha了,ETC2的效果也都还不错,回过头看,发现苹果系统才是目前的症结所在。其实苹果的透明图压缩质量一直都很差,只是为了满足安卓系统,透明图一般都会拆通道(或者干脆不压缩),不想对iOS单独处理就干脆全平台一起做了,使得iOS的问题并不明显。
然而现在安卓平台的问题都解决了,iOS的问题就显露了出来,虽然iOS也有了新格式ASTC,苦于普及率远不如ETC2(貌似是IP6以上才支持),想要遗弃掉PVRTC可能还得再等上一年。所以现阶段,我们只能选择单独对iOS平台拆分UI图集的透明通道,来解决UI压缩后完全不能看的问题。
一、工程文件
链接地址:https://github.com/flashyiyi/UGUIAlphaAtlas
- Editor/CreateAlphaAtlas.cs 创建透明通道图的编辑器脚本
- Editor/CustomPackerPolicy.cs 自定义图集生成规则
- AlphaAtlasManager.cs 透明通道图管理类
- SplitImage.cs 可以显示透明通道的Image
二、生成Alpha图集
首先需要切换到iOS平台,然后把图集里的图片格式设置为PVRTC RGBA(脚本现在只会影响这个格式的图集)
再执行Tools下的这个命令:
便会在Resources/TextureAlphaAtlas目录下生成透明图集图片和配置文件:
注意,执行这个命令之后,图集生成规则文件会自动更换为CustomPackerPolicy。它唯一的作用只是让原本是PVRTC RGBA的零散Sprite,在生成最终图集的自动转换成PVRTC RGB。
if (settings.format == TextureFormat.PVRTC_RGBA4 && forceIOSOpaque) //强制生成不透明图集
settings.format = TextureFormat.PVRTC_RGB4;
必须保留这个配置文件,iOS下才是正确的。而在其他平台下,CustomPackerPolicy和默认的DefaultPackerPolicy并没有区别。但如果你曾经修改过图集规则,就需要注意下这个变化。
三、让透明图集生效
方法1:将之前生成的图集文件打入包内,再将所有的Image替换成专门的SplitImage.cs。这个SplitImage.cs重写了Image两个函数,会在运行期间加载透明图集。
public class SplitImage : Image {
public override Material material
{
get
{
if (m_Material != null)
return m_Material;
if (overrideSprite && (overrideSprite.associatedAlphaSplitTexture != null || AlphaAtlasManager.GetInstance().GetAlphaTexture(overrideSprite) != null))
return defaultETC1GraphicMaterial;
return defaultMaterial;
}
set
{
base.material = value;
}
}
protected override void UpdateMaterial()
{
base.UpdateMaterial();
Texture2D alphaTex = AlphaAtlasManager.GetInstance().GetAlphaTexture(overrideSprite);
if (alphaTex != null)
canvasRenderer.SetAlphaTexture(alphaTex);
} }
结果:
目前文件都是用Resources.Load直接加载的,可以修改AlphaAtlasManager里的加载逻辑以便引入AssetBundles机制。
方法2(推荐)
PackAlphaAltas命令相当于一个打包AssetBoundles的预处理,它会在打包前手动修改Sprite的RD属性,将透明图挂在Sprite上。这样直接用原始的Image就能显示拆分通道后的Sprite了。
SerializedObject so = new SerializedObject(sprite);
so.FindProperty("m_RD.textureRect").rectValue = GetAltasTextureRect(sprite, atlasTexture);
so.FindProperty("m_RD.texture").objectReferenceValue = atlasTexture;
Texture2D alphaTexture = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/" + AlphaAtlasManager.TEXTURE_ALPHA_ATLAS_PATH + atlasTexture.name + "_alpha.png");
so.FindProperty("m_RD.alphaTexture").objectReferenceValue = alphaTexture;
so.ApplyModifiedProperties();
不过这会影响到项目原有的打包流程。可以把方法内的BuildPipeline.BuildAssetBundles部分替换成项目的打包方法,再调用PackAlphaAltas来打包,把两部分合并在一起。
EditorSettings.spritePackerMode = SpritePackerMode.Disabled;
BuildPipeline.BuildAssetBundles(Application.dataPath + "/AssetBundles", BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.iOS);
EditorSettings.spritePackerMode = SpritePackerMode.AlwaysOn;
这个方案和安卓平台下的ETC1+Alpha表现是一致的,加载策略也相同。并不需要将图集文件手动打入包内,也不需要做额外的管理。AlphaAtlasManager.cs,SplitImage.cs这两个文件实际上可以删除。这样可以保证多平台逻辑的一致性。
四、延伸思考
虽然我们在这里使用的是Unity的自动图集,但在最后的打包过程里,实际上已经把自动图集功能关掉了,是靠手动修改数据文件,修改图片链接和textureRect来产生的图集效果。所以,实际上这个做法也可以延用到TP上,修改赋值代码即可。
Unity自动图集最主要的优点,是可以一开始的时候不打图集,在最后才考虑图集的事情。也因为这个原因,修改图集归属是无成本的,而这正是TP的缺点。所以用同样的方法,在最后才接入TP的图集和结构数据,就能够综合两者的优点,扬长避短。
不过,TP比Unity图集多的功能也就是精灵的90度旋转了,紧密布局则是双方共有的(Unity2017的新版图集 & 自带图集实现TP的Polygon布局https://zhuanlan.zhihu.com/p/32591450)。而且这些功能都需要修改UI组件才能获得支持,也并不必要。
本来就不使用TP的项目,倒也没必要引用一个大部分功能重复的三方库进来。
这是侑虎科技第366原创文章,感谢作者flashyiyi供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)
作者知乎:https://zhuanlan.zhihu.com/p/32674470,同时,作者也是U Sparkle活动参与者哦,UWA欢迎更多开发朋友加入U Sparkle开发者计划,这个舞台有你更精彩!
IOS不是用的RGBA Compressed PVRTC 4bit吗?