1)移动平台纹理压缩格式选择
2)Unity 2018是否在Mali GPU上支持Alpha 8格式
3)如何在Unity自带的Navmesh上获取地面高度
4)ParticleSystem无法重新播放
5)UI开发中按界面的打开顺序返回到上级面板的问题
Texture
Q:在这之前了解过纹理压缩的相关知识和UWA的一些推荐方式。但还是有一点小的疑问,所以在这里再次提出来,希望得到解答。
在纹理压缩格式的选择上,如果Android选用ETC,iOS选用PVRTC,因为有2的次方(ETC1和PVRTC)长宽相等(PVRTC)的要求。
问题1:大家对于美术出图的大小要求是怎样的?
- 出图就出成2的次方。
- 出图不是2的次方,然后通过Unity默认的NPOT的选项让它自动转换了。
- 其它方式?
问题2:上述1和2两个方式有本质上的区别吗?看到UWA上Xin大提到了直接使用NPOT的方式(3年前的问题了):
https://answer.uwa4d.com/question/58d2943ae00cc20065a42597
问题3:2的次方要求极端情况会导致ETC2(4的倍数要求,但考虑到如果要使用PVRTC)的内存占用变得很大,这种情况选择ETC2还是RGBA16?
2的次方的要求极端情况会导致宽高都扩大接近一倍(比如:300*300往大的变会变成512*512),这样算下来多数情况如果带Alpha,512*512的ETC2-RGBA比300*300的RGBA16的内存占用还要高。
问题4:大家在纹理压缩格式的选择上,现在(2019年)的项目都是怎样选择的?
ASTC考虑设备的普及情况,Android暂时没有考虑,iOS在考虑范围内(A8要求)。
A:先说问题4吧,这个会影响之前的问题。
2019年,我觉得*的产品iOS上ASTC已经没什么问题了。iPhone 5s以及之前的设备就算了吧,内存就够玩的了(当然还要看游戏类型,休闲小游戏还是要考虑更多兼容)。放弃的另外一块是iPad mini和老的iPad,iPad的更新频率比较低,所以老设备多一些,但是市场占有率整体也低。
Android我觉得ETC2够用了,像normal等特殊贴图用特殊的压缩方式,也没必要强上ASTC。
iOS:2018年上线的项目强上了ASTC,主要是因为UI不接受PVRTC的压缩效果。
这种选择的情况下:
问题1:方形就不用考虑了,不重要。POT建议遵守,这种对于GPU性能有好处,通过合图以及美术规范就可以处理了。问题2:大部分情况下没有本质区别,只要贴图按照UV采样就没问题。个人倾向于让POT成为美术基本的出图规范(特殊的Loading图等除外)。另外就是美术,如果知道可以用到更大尺寸而消耗又一样,也许可以增加一些细节。
问题3:RGBA16如果效果不失真可以考虑。话说300*300这种,你可以选择搞成256*256,但是整体肯定还是推荐压缩的格式,大部分情况下都比RGBA16要好一些(内存消耗+效果整体考虑)。
一般来说,UI大部分会合图成POT的,3D的部分都以POT的方式设计和制作,这样整体的规范比较好制定。特殊的部分才用NPOT的,也只会影响比较少的部分。
感谢贾伟昊@UWA问答社区提供了回答
Texture
Q:测试机型OPPO A3,贴图大小1024*1024
测试样例一:
Unity 2017.4.16 / 21张 Alpha 8图片
测试结果:
测试样例二:
Unity 2018.3.13 / 11张 Alpha 8图片 / 11张 R8图片
测试结果:
根据第一张图片对比,可以看到Unity 2017.4.16不支持Alpha 8格式,而Unity 2018.3.13的格式支持Alpha 8格式贴图,根据第二张图片的Graphics内存大小验证发现Unity 2017上Alpha 8格式已经FallBack到4MB的RGBA32,而Unity 2018上Alpha 8格式内存还是保持在1MB。同时我用SnapDragon Profiler抓帧小米5机器发现Unity 2018打包的APK中Alpha 8格式被转换成R8格式。
简而言之:Unity 2017.4.16打包的APK中不支持Alpha 8并且Fallback为RGBA32格式,Unity 2018.3.13打包的APK中支持Alpha 8,而且用SnapDragon Profiler抓帧发现在GPU中以R8格式存在。
问题一:Unity 2018.3.13能支持Alpha 8格式贴图?
问题二:Unity 2018的R8格式在iOS或者Android上是否对硬件有要求?
A:问题1:
Unity在GLES3对于单通道Alpha 8的图,其实都是会变成R8的,这也就是为什么题主截出来的图是R8的。为了支持在Shader中可以使用Alpha通道获取R8贴图中R通道的数据,就需要依赖于Texture Swizzle机制。不幸的是Unity 2018针对GPU为Mali并且系统版本在Marshmallow及以上的机器,都认定Texture Swizzle机制存在问题。所以Unity会将Alpha 8直接Fallback到了RGBA格式上,导致GPU上对应贴图内存变为4倍。问题2:
1)Unity对于GLES 2.0,直接支持Alpha 8的图,应该不需要考虑转到R8的问题了。当然R8本身也是支持的。
2)对于iOS,我还没测试过,就不下定论了。
感谢Salt@UWA问答社区提供了回答
Navmesh
Q:在人物做移动的时候,都不会使用重力、刚体之类的,玩家想去一个点,y的坐标就得获取地形的高度,对此有什么好的办法吗?
A:把地形数据存为二进制,然后就能根据x、y得到高度了。可以把是否能行走记录下来,整体记录到一个大的二维数组中,然后直接存储到本地文件。读取的时候根据x、z直接去索引二维数组的对应地图数据。
感谢Crazy_Liu@UWA问答社区提供了回答
ParticleSystem
Q:在对特效设置显隐中,不使用SetActive true/false, 而是移到屏幕之外,然后对animator enable true/false,以期减少Animator instance开销。
届时特效中附带的粒子无法重新播放,导致特效从屏幕外移回屏幕内时,特效上粒子都丢失。附上代码和特效。
{ if (obj == null) return; // //显示隐藏暂时遇到粒子系统无法重新播放的问题, 暂时用原有方式 // obj.SetActive(bActive); // return; Vector3 pos = obj.transform.localPosition; Animator[] Anis = obj.GetComponentsInChildren<Animator>(); //没有animator 显影直接SetActive true/fasle if(Anis.Length <= 0) { obj.SetActive(bActive); return; } if (bActive) { if (pos.x > 10000) { pos.x = pos.x - 10000; } } else { if (pos.x < 10000) { pos.x = pos.x + 10000; } } for (int i = 0, count = Anis.Length; i < count; i++) { Anis[i].enabled = bActive; if (bActive) { AnimatorStateInfo anif = Anis[i].GetCurrentAnimatorStateInfo(0); Anis[i].Play(anif.nameHash, 0, 0); } } // if(bActive) { ParticleSystem[] pars = obj.GetComponentsInChildren<ParticleSystem>(); for(int i = 0, count = pars.Length; i<count;i++) { if (bActive) pars[i].Play(); else pars[i].Pause(); } } obj.transform.localPosition = pos; }
A:下面的写法暂时没遇到你说的情况:
// 开始播放 foreach (var particleSystem in m_ParticleSystems) { particleSystem.time = 0; particleSystem.Play(false); // false参数不递归子物体 } // 停止播放 foreach (var particleSystem in m_ParticleSystems) { particleSystem.Clear(false); particleSystem.Stop(false); }
感谢张迪@UWA问答社区提供了回答
UI
Q:UI开发中界面返回的方法我是用栈的方式实现的,会把面板信息存到栈中,但是会出现要打开的新面板已经在栈中存在,这种是要怎么处理,还有可能会出现成环的问题,就是类似这种 A→B→C→A→B→C ,这种要怎么处理?怎么判断是否成一个环?设计上是可以避免,但是如果出现了要怎么处理?求各位大佬帮帮忙。
A1:如果做了完备的数据和表现分离,将当前界面状态,比如选中的Tab页、滚动到的位置等信息存储下来,栈内存储的是界面ID和这些信息,做支持已经存在的栈是完全可以支持的。
当然,你也可以和策划沟通来确认。如果出现你描述的新面板在栈中已经存在如何处理,比如:是否可以就从栈中删除然后添加到新的位置,逻辑上也可以,只是确定表现是否合适。
最后,栈记录的是玩家之前看过的界面,即便有环,也不是无尽的环,另外这个栈通常会有一个上限,配合一个清空时机,来避免内存占用过多之类的问题。
感谢贾伟昊@UWA问答社区提供了回答
A2:一般界面是分层级设计的,比如:一级面板,二级面板,三级面板,四级面板,Tips面板。同级面板只允许同时打开一个或左右各一个。
从高层级面板跳转打开低层级面板时,直接清空低层级之上的栈数据。比如:A是一级面板,B是二级面板,C是三级面板。从C跳转打开A时,直接清空二三级数据,并替换一层级面板的数据。
如果要检测也可以,直接从底部扫描一下,发现有相同的,就清除这个位置以上的。比如:A–>B–>C 这时打开A,发现第0个位置已经存在A了,就直接删除位置1以上的节点。
不要用栈,直接用数组会更方便。
感谢kk@UWA问答社区提供了回答
今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。
官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)