移动平台纹理压缩格式选择

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(原群已满员)

移动平台纹理压缩格式选择

上一篇:Android7.1 屏幕旋转


下一篇:Android Gradle Plugin v3.6.0/3.6.1 构建Bug