作者 / Ben Gable, Partner Developer Advocate
让我们继续《让您的应用适配更多屏幕》中的话题,不过今天会更专注于游戏的范畴。在不久前的 GDC 上,我们很高兴能和大家分享多样化的设备为游戏体验带来的可能性。这些年我们亲历了诸多移动硬件的演进,如 3D 显示器和高分辨率屏幕等,也因为这些演进,让我们在应用商店中看到越来越多新颖的游戏出现,让玩家们在更沉浸的画面和游戏体验中流连忘返。
如果能在更大的屏幕上体验的话,这些游戏也会拥有更好的视觉效果。这意味着开发者可以通过针对不同的外形和更大的显示设备优化游戏,来吸引正在快速增长的大量受众群体。
如果您在应用商店上架了游戏,那它可能已经可以在 Chrome OS 上运行了。Chromebook 在一个容器中运行完整版的 Android 框架,因此游戏的体验就像在 Android 设备上运行一样。但这并不是终点,通过进行一些调整,开发者可以进一步针对性地优化游戏,使其在大屏幕设备上拥有最佳的视觉效果和运行性能,并为可折叠设备上的最佳游戏体验奠定基础。
GDC 期间我们的很多演讲都涉及到了这个话题。这里我们为您系统地整理了一遍,并给出了能让您立刻上手的代码示例和开发技巧。
大屏幕游戏的常见问题
作为游戏开发者,您当然想要在每台设备上都提供最佳的游戏体验——无论用户是在使用竖屏模式,还是使用键鼠,甚至在可折叠设备 (拥有更多可切换的分辨率和宽高比配置) 上进行游戏。通过遵循 Android 最佳实践,开发者可以在所有这些使用场景中都提供出色的体验。
在 APK 中,适配的所有操作都是从配置 (configuration) 的变动开始。系统在检测到设备配置的变动后,会自动重启您的 Activity 并在这个过程中调用适配新设备配置的游戏资源,从而确保您的游戏在该配置下达到理想的效果。
在默认情况下,系统将在重新启动 Activity 之前通过调用 onSaveInstanceState 自动处理配置更改。但对于素材量很大的游戏来说,这一过程可能需要更长的时间,尤其是在涉及到状态数据的序列化和反序列化时,可能会因为大量的内存消耗而出现迟滞现象,因此您可能需要自己对这一步操作进行针对性的改进,比如让系统在特定的配置变化之后不重启 Activity,而是调用 onConfigurationChanged 方法让您自行处理适配细节:
public void onConfigurationChanged(Configuration newConfig) {
//Scale UI
//Toggle control scheme
//Adjust orientation
//etc
}
进一步了解运行时变更,以及状态恢复
https://developer.android.google.cn/guide/topics/resources/runtime-changes.html
屏幕和窗口管理
在 Chromebook 和三星 DeX 等设备上,游戏默认在窗口模式中启动,用户可以动态调整画面大小;在三星 Galaxy Fold 等设备上,当用户展开设备时,屏幕尺寸和宽高比将会改变。这些设备配置变化的场景都可以触发 onConfigurationChanged 事件。
您可以在 manifest 中这样指定需要监听的配置变动项目:
<activity android:name=".MyActivity"
android:configChanges="screenSize|orientation"
android:label="@string/app_name">
正如前文所述,在游戏运行时如果发生配置更改,默认情况下系统会关闭 Activity 然后将其重新启动,但如果您使用 android:configChanges 属性声明了需要监听的配置,将会阻止 Activity 重新启动。这时 Activity 反而会保持运行状态,并且系统会调用其 onConfigurationChanged() 方法。如此一来,您就可以定制化地处理任何变化了。例如,您可以缩放 OpenGL 内容以匹配新的分辨率或宽高比,防止 UI 元素被画面裁切。
比如当用户动态调节画面大小时:
public void onConfigurationChanged(Configuration newConfig) {
//Get native surface size
//NativeActivity.mLastContent[X/Y/Width/Height]()
//findViewById(android.R.id.content).get[Width/Height]()
}
有一点需要牢记,那就是每次画面尺寸发生变化时,输入区域也会发生变化。如果您的游戏打算保持全屏运行,或者您需要在修 bug 时快速略过配置更改问题,则只需在 manifest 中将 resizableActivity 设置为 false 即可。这一操作将阻止用户手动调整画面大小,但它也会阻止分屏模式 (split-screen view),因此请谨慎使用,以避免影响玩家体验。
<activity android:name=".MyActivity"
android:resizeableActivity="false"
android:label="@string/app_name">
△ 将 resizableActivity 设置为 false 可以强制游戏处于全屏模式
设备方向
在智能手机上,屏幕方向默认为纵向 (竖屏)。而在其他设备上,默认值则有可能是横向。请在 manifest 中指定您支持的传感器方向,让系统处理它们,从而确保您的游戏不会意外翻转。
<activity … android:screenOrientation=”...”>
或者,如果您打算在 onConfigurationChanged() 中动态处理方向,请在代码中使用 Display.getRotation() 获取当前屏幕方向。
Display.getRotation()
△ 屏幕方向的数值可能会在不同的设备上互换,具体取决于该硬件默认的设备方向值,请一定要在测试中考虑到这一点
设备分辨率
在解决了上面两个问题后,您还需要考虑用户设备的分辨率。较大的屏幕需要更高分辨率的素材资源,因此您可以使用应用束 (App Bundle) 来为不同的设备配置分割打包游戏的代码和资源。这样,Google Play 就会为游戏所在的设备提供正确的资源——例如,为较小屏幕的设备略去超高分辨率素材。这将为用户节省宝贵的下载空间,同时也免去了为不同像素密度的屏幕维护多个 APK 的麻烦。
△ App Bundle 可以针对不同配置或功能分割打包代码和资源,使得 Play Store 能根据用户的实际设备进行动态交付
另外,更多的屏幕像素也可以为画面和界面设计赋予更大的灵活性。当用户在更大的屏幕上玩游戏时,他们可能会坐得离屏幕更远,这时您需要考虑扩展、添加或更改画面 / 界面元素,以确保屏幕上的所有内容都清晰可辨。
输入设备
越来越多的手机游戏开始为玩家提供如同台式机和主机一般的体验。所以,越来越多的 Android 设备开始支持各种不同的输入方式,包括键鼠和手柄等等。尽管 Android 提供了支持键盘、鼠标和手柄的 API,但想要这些额外的输入方式真正可用,还是需要游戏开发者们更多地理解玩家的游玩诉求。比如玩家可能在没有触摸屏的 Chromebook 上用键盘玩游戏,或者打算插上鼠标来玩自己最喜欢的第一人称射击游戏,这些时候如果玩家发现自己期待的操作方式没有被游戏支持,失望之情也就不难理解了。
另外,许多新设备也支持在使用时动态切换配置,例如从折叠模式转换到平板电脑模式。想要支持这些场景,最佳的方法是检测设备上可用的硬件,以便为玩家提供最佳体验。
要做到这一点,还是要从 android:configChanges 开始。例如检测键盘的连接:
然后,您可以在启动时或在配置更改时查询 InputManager,并根据触摸屏或者键盘的可用与否来切换控制方案。另外,当从触摸控制方案切换成键盘控制方案时,即使 InputManager 说键盘可用,您也需要进一步确保键盘真的可用 (比如在画面上提示用户 "按任意键继续")。毕竟,用户这时用的可能是一台平板电脑模式下的 Chromebook。
△ 切换操作模式时,一个推荐的做法是暂停游戏并让用户使用对应的输入设备进行一次确认操作
如果您是在游戏做完后才打算支持键盘,请考虑实现下面的功能,这样可以大幅提高可用性和完成度:
技能键绑定
WASD / 方向键移动
菜单导航
Enter 键输入
Tab 键切换 (依游戏所需)
上下翻页键功能
返回键
自定义键位映射
还有,别忘记使用鼠标的玩家——特别是在第一人称射击游戏或第三人称游戏里。
public void onClick(View view) {
view.requestPointerCapture();
}
public boolean onCapturedPointerEvent(MotionEvent motionEvent) {
// Get the coordinates required by your app
float verticalOffset = motionEvent.getY();
float horizontalOffset = motionEvent.getX();
return true;
}
//...
view.releasePointerCapture();
最后,考虑加入对手柄的支持,因为标准的 Android 手柄 API 适用于 Chromebook 和三星 DeX 等设备——这些都为使用手柄提供了可能性。
构建
现在,让我们继续构建和配置您的 APK,以确保它支持各种大屏幕 Android 设备。
第一步是检查您的游戏所要求的权限,以及确定您是否真的需要这些权限。某些设备 (如 Chrome OS) 就不支持某些权限,例如:
android.hardware.location.gps—— GPS
android.hardware.nfc——近场通信 (NFC)
android.hardware.camera——后置摄像头
另外,请考虑运行游戏的硬件配置,并考虑调整以下内容:
自动画质控制: 调整自动画质控制逻辑或创建新的画质配置,以便针对特定设备进行最佳优化。
x86 和 ARM: 尝试提供 x86 版本,以便在 Chromebook 等设备上实现最佳性能。只有 ARM 版本的话游戏仍然可以运行,但是您会因为指令翻译而产生额外的性能开销。
对 Vulkan 的支持: 大多数设备现在都支持 Vulkan,它可以大大提高渲染速度和图形表现。
最后一步是在所有大屏幕设备上测试。添加涵盖不同设备的、更多样游玩流程的测试用例,例如最小化最大化、在小屏幕和大屏幕之间切换、变更输入设备和调整窗口大小。您可以使用 Android 和 Chrome OS 模拟器或 Firebase Test Lab 等工具来让测试过程变得更加自动化。
您也可以使用在 Android 设备和 Android Studio AVD 上运行的折叠屏模拟器应用,来测试不同窗口大小和像素密度时的运行状况:
$ adb install FoldableEmulator.apk
$ adb shell pm grant com.samsung.android.foldable.emulator android.permission.WRITE_SECURE_SETTINGS
$ adb shell pm grant com.samsung.android.foldable.emulator android.permission.SYSTEM_ALERT_WINDOW
了解有关可折叠设备测试的更多信息
https://developer.samsung.com/galaxy/foldable/test
可折叠设备
可折叠智能手机通过将智能手机和平板电脑合二为一,让游戏玩家可以鱼与熊掌兼得: 可以做到多窗口操作,也可以让游戏画面的尺寸倍增。
为 Chromebook 构建的应用在优化原则上和可折叠设备高度相通。因此在完成前面的优化适配后,您只需考虑以下四个关键事项:
最大宽高比
支持全屏模式对于确保游戏玩家获得最佳的沉浸式体验至关重要。可折叠设备在折叠起来时画面将拥有较长的宽高比 (可达 21:9),因此请执行以下步骤,以确保您的游戏可以处理最大宽高比,从而填充整个屏幕:
声明目标 SDK 版本: 以 Android 8.0 (API 级别 26) 或更高级别为目标的游戏可以填满整个屏幕。
声明 resizeableActivity (如果您的游戏支持多窗口,则只声明为 "true"): android:resizeableActivity=[“true” | “false”]
声明最大宽高比: 可折叠设备宽高比可达 21:9,这要求您将最大宽高比提高到 2.4。
您可以按照如下示例在 manifest 中声明最大宽高比:
<!-- Render on full screen up to aspect ratio of 2.4 -->
<!-- Use a letterbox on screens larger than 2.4 -->
<meta-data android:name="android.max_aspect" android:value="2.4" />
△ 设置最大宽高比为 2.4,如果宽高比超过 2.4,则左右两侧会留空
处理屏幕凹口区域
以 Galaxy Fold 为例,在展开状态时,其屏幕的左上角会有一个凹口,您需要保证游戏画面中的必要内容不被遮挡。
如果您的游戏在渲染时会覆盖到凹口区域,可以使用 WindowInsets.getDisplayCutout() 来获得 DisplayCutout 对象。这里有三个值需要注意:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
MODE_DEFAULT: 内容在竖屏模式时会在凹口区域内渲染,但在横屏模式下左右会留空。
MODE_SHORT_EDGES: 不论横竖屏模式,内容都会在凹口区域内渲染。
MODE_NEVER: 内容永远不会在凹口区域内渲染。
您可以参考如下示例来给您的 Activity 设置凹口渲染模式:
<style name="ActivityTheme">
<item name="android:windowLayoutInDisplayCutoutMode">
shortEdges <!-- default, shortEdges, never -->
</item>
</style>
△ 直接将 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 应用于 Activity
了解更多针对凹口屏幕开发的技巧
https://developer.android.google.cn/guide/topics/display-cutout
游戏连续性
当用户展开他们的设备时,您需要确保您的游戏无缝过渡到全尺寸状态。这意味着您的游戏可以适应两种实体屏幕尺寸,对实体设备折叠机制及其状态作出正确响应,并且能够调整画面大小。
1. 确保您的游戏可以处理两种屏幕尺寸,这个机制与在 Android 上适配不同屏幕尺寸的方法基本相同。唯一的区别是,这种情况在可折叠设备上会更频繁地发生。
适配不同屏幕尺寸
https://developer.android.google.cn/training/multiscreen/screensizes.html
2. 处理折叠和展开,开发者不需要针对 Galaxy Fold 这样的设备处理特殊事件或 API,因为这个过程就是 Android 平台标准的屏幕尺寸变化事件。更具体到 Activity 的 configChanges 上,会触发这么几个变化: Screensize、smallestScreenSize、ScreenLayout。
了解 configChanges
https://developer.android.google.cn/guide/topics/manifest/activity-element?hl=zh-CN#config
3. 让游戏画面尺寸实现自适应,以在不同显示模式之间灵活切换,从而让用户在折叠和展开屏幕时都能体验到无缝的切换过程。
多重恢复
正如我们之前在适配可折叠设备的话题中提到过的,多重恢复 (Multi-resume) 意味着在多窗口模式时让所有可见的 Activity 处于 resumed 状态。在 Android 9 Pie 中,游戏和应用都可以选择打开 Multi-resume 功能,只需将声明添加到 manifest 中即可:
<application>
<meta-data
android:name="android.allow_multiple_resumed_activities"
android:value=”true” />
<activity ... />
</application>
成功案例: Gameloft Asphalt 9
在了解到玩家们的大屏幕游戏需求后,Gameloft 利用这个契机调整了 Asphalt 9 的游戏设置,以便优化这款游戏在大屏幕设备上的体验。团队在构建游戏时考虑了各种输入方式和显示器尺寸,并确保了游戏在不同种类的设备上拥有一致的高速性能表现。
Gameloft 在 GDC 2019 上的分享
https://www.youtube.com/watch?v=ZfBeab5u534
我们还与 Gameloft 合作,针对Chrome OS 和可折叠设备优化了 Asphalt 9 的前作,Asphalt 8: Revolution。
针对 Chrome OS 进行适配后,Asphalt 8 的每日活跃用户数量增加了 6 倍,Chrome 用户带来的收入则增长了 9 倍。也正是在看到适配带来的优势后,Gameloft 的核心工作就涵盖了为较大屏幕进行设计的要素。
打造移动游戏的未来
我们希望您在阅读完本文后,能认识到 Android 游戏不再是专属于移动设备的体验——今天的玩家正在各种外形和尺寸的设备上体验游戏。通过遵循 Android 最佳实践并额外投入一些时间思考您的游戏在大屏幕上体验的可能性,您可以将您的游戏带到更大的舞台上,真正做到为每个玩家提供最佳的游戏体验。
您的游戏有适配更多的屏幕尺寸吗?欢迎在评论区和我们分享游戏适配中的疑问和心得。
点击屏末 | 阅读原文 | 即刻开始适配更多屏幕
推荐阅读