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

侑虎科技 · 2020年05月25日 · 1752 次阅读

1)移动平台纹理压缩格式选择
2)Unity 2018 是否在 Mali GPU 上支持 Alpha 8 格式
3)如何在 Unity 自带的 Navmesh 上获取地面高度
4)ParticleSystem 无法重新播放
5)UI 开发中按界面的打开顺序返回到上级面板的问题


UWA 问答社区:answer.uwa4d.com
UWA QQ 群 2:793972859(原群已满员)

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 问答社区提供了回答


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 问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之 “石”,也能攻你之 “玉”。

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册