上一篇文章介绍了 Shopee Games 团队如何选择游戏引擎,扩展游戏引擎以提高生产效率,让游戏开发流程和成熟的前端工程化体系结合,实现游戏规范化和研发质量的提升。
在 Shopee Games 的游戏引擎演进之路上,我们也遇到并解决了一些研发当中的问题,本文将针对 iOS 审核问题,分享我们将 Egret Native 与 Shopee App 结合的解决方案。
在 Shopee Games 推出早期,用户量和访问量都不大,苹果公司没有着重针对 HTML5 版本的 Shopee Games 提出意见。但是,随着 Shopee 业务不断发展,用户量和访问量持续增加,2021 年初,苹果公司对 Shopee 内嵌的 HTML5 游戏提出了意见,认为 Shopee App 违反了《App Store Review Guidelines》的 4.7 条款。
苹果公司的审查条款原文是这样的:
4.7.1 Software offered under this rule must:
- be free or purchased using in-app purchase;
- only use capabilities available in a standard WebKit view (e.g. it must open and run natively in Safari without modifications or additional software); and use WebKit and JavaScript Core to run third-party software and should not attempt to extend or expose native platform APIs to third-party software;
- be offered by developers that have joined the Apple Developer Program and signed the Apple Developer Program License Agreement;
- not provide access to real money gaming, lotteries, or charitable donations;
- adhere to the terms of these App Store Review Guidelines (e.g. do not include objectionable content); and
- not offer digital goods or services for sale.
归纳起来,包含以下几点要求:
而 Shopee Games 内嵌在 Shopee App 内,天生需要和 Shopee 深度结合,必然涉及 JSBridge (例如登录、跳转电商店铺)。并且,Shopee Games 还使用了 Shopee Coins 作为游戏中的货币,而 Shopee Coins 并不是通过苹果支付得到的,是用户在电商购物中累积获取的。从这两个角度来看,Shopee Games 使用 HTML5 技术必然触犯以上的苹果审查条款。
我们参考了 Egret 引擎官方的建议,改变了 iOS 平台上 Shopee Games 的技术架构,从原来的 HTML5 改为了 Native,使用的是 Egret Native Runtime。Egret 官方工具支持一键生成 iOS 工程并发布对应的 App 安装包,这能够满足独立游戏的需求。但是 Shopee Games 需要内嵌在 Shopee App 中,并不是独立的游戏 App,所以无法直接简单使用官方的一键发布机制,我们需要进一步研究 Egret Native 的原理,从而和 Shopee App 做整合。
分析 Egret Native 工具创建的 iOS 模板工程,可以发现:
虽然 Egret Native 并不开源,但根据官方文档,再结合模拟器断点调试分析,还是可以对 Egret Native 有进一步的发现。
Egret Native 游戏架构包括三层:前端游戏层、Egret Native Runtime 和 iOS Native 层。
启动 Egret Native 游戏时,先从 iOS Native 层初始化 Egret Native 实例,并创建对应的游戏 view,挂载到主界面上。然后,Egret Native 初始化 JS 引擎,绑定 JSBridge,读取前端游戏层的游戏资源,解析 HTML 和 JS,调用 OpenGL 接口,最终显示游戏画面。
从模板工程来看,把 Egret Native 游戏内嵌到 Shopee App 的方式是比较清晰的,把 libEgretNativeIOS.a 和其他必要的依赖库添加到 Shopee App 项目工程中,再把游戏包存放到 assets 目录中,最后绑定必须的 Shopee JSBridge,供游戏逻辑实现登录、支付、跳转等操作,整个结合工作就完整了。
但是,在最终实现过程中,发现一些深层次问题,需要通过业务侧的方式解决或规避。这些问题包括:
1)模板工程不支持多个游戏
Shopee Games 包含多个游戏,而上述 assets 目录只支持存放一个游戏的资源。并且 Egret Native Runtime,也就是上述的 libEgretNativeIOS.a 没有开源,无法做针对性的修改。
最后,我们在官方的《热更新方案说明》中,发现了 Egret Native 可以设置 preload 目录路径,而 preload 目录内的资源优先级比 assets 目录高。
于是,我们可以在 Shopee App 内置多个游戏,分别存放于一个单独的目录中,在启动 Egret Native 前设置 preload 路径为对应游戏的路径。Egret Native 启动后会优先读取 preload 目录的资源来启动游戏,忽略后续 assets 的内容。
2)网络缓存文件无限增大
Egret Native Runtime 对网络请求做了缓存,但没有完善的清理机制,导致本地缓存目录会随着游戏下载的资源增多而无限增大。这部分需要在 Shopee App iOS Native 逻辑层面进行补齐。
3)部分第三方库命名冲突
libEgretNativeIOS.a 自带了一些第三方库,例如 SocketRocket,而 Shopee App 也引入了这个库,双方都没有对这个库做别名处理,导致命名冲突编译失败。由于 libEgretNativeIOS.a 无法修改,只能在 Shopee App 业务层面自行修改 SocketRocket。
4)Egret Native 存在内存泄漏
由于在 Shopee App 中,Egret Native Runtime 需要反复启动和销毁,只要 Egret Native Runtime 对对象、纹理处理稍有不当,都会引起内存泄漏。而对于 Egret Native 独立游戏来说,这个问题就不存在,因为每次关闭游戏都是整个 App 的销毁。这个问题已经反馈给 Egret 官方,但由于官方团队不会专门针对我们的使用场景做处理,所以目前暂无进展。庆幸的是,这部分内存泄漏很小,暂时不构成大的问题。
通过解决上述一系列问题,最终我们实现了 Egret Native 和 Shopee App 的结合,能够在 Shopee App 上运行多款自研游戏。在苹果审核的角度,我们这个方式脱离了 Webkit,而且所有代码逻辑内置在提审的安装包中,完全符合审查条款的要求。因此,方案上线后,Shopee App 和 Shopee Games 顺利通过了 App Store 的审核。
虽然 Egret Native 已经能够结合在 Shopee App 中,但在持续运营半年后,我们发现 Egret Native 还是存在一些问题:
于是,我们正在筹划更长远的解决方案——自研的 Native Runtime,能够同时支持多家 H5 游戏引擎。
这个解决方案采用类似微信小游戏的整体架构,但在 JS 层面会做进一步的封装,方便 Shopee 内各个游戏团队快速接入。技术层面概要来说,就是 Native 基于 JS 引擎向 JS 侧暴露对齐 WebGL 标准的接口和必要的 BOM 接口,从而普通 H5 游戏引擎就可以无缝运行在浏览器和我们自研的 Runtime 上。
这个方案的好处有:
但是,相应也存在劣势:
目前 iOS 已经迭代到 15,OpenGL 和 WebGL 还是能够平稳运行,而要支持多款 H5 游戏引擎,WebGL 标准是无法绕过的,所以这些劣势暂时不得不接受。另外,微信小游戏也会面临一样的问题,相信未来会有苹果官方的迁移方案,可能在 Metal 上层封装类 OpenGL 的接口。
目前这套方案还在预研开发中,预计 2022 年内会实现并全面使用。之后不单是 Shopee Games,我们希望在更多的电商场景也能复用这套 3D/GPU 渲染的方案,给用户创造更丰富多彩的互动玩法。