为 Android 开发游戏时,预测玩家可能遇到的各种体验并适应玩家的实时互动需求非常重要。通过支持不同的玩家体验,您可以增加游戏的玩法灵活性,帮助您扩大游戏的覆盖范围。
玩家体验的具体差异包括:
- 设备外形:尽管手机提供传统的 Android 设备体验,但游戏可以在其他外形设备上进行交互。ChromeOS 设备可以运行一个显示您游戏的 Android 容器。可以运行 Android 的平板电脑支持多种不同级别的保真度。Android TV 设备支持更细节丰富、更沉浸式的体验。玩家可以使用显示扩展工具模拟多窗口环境。当使用可折叠设备时,玩家可以在游戏会话期间更改屏幕大小。
- 交互方法:玩家可以通过触摸设备屏幕提供输入,但他们也可以使用鼠标、触控板、键盘或控制器。此外,显示扩展工具和可折叠设备的可用性允许玩家在更大的屏幕上体验您的游戏,使更长的游戏会话和更复杂的界面成为可能。
- 硬件支持:一些 Android 设备没有手持设备中更典型的硬件,例如后置摄像头、GPS 和网络连接。您的游戏应该适应可用的硬件,并优雅地处理某些功能不可用的情况。
本指南介绍了针对不同类型的屏幕和用户交互开发游戏的最佳实践。本指南还提供了游戏设计和开发有效测试策略的建议。
游戏设计最佳实践
在规划游戏的设计和架构时,请遵循以下部分描述的最佳实践。
手动响应配置更改
当 Android 系统检测到配置更改(例如屏幕尺寸、屏幕方向或输入法的更改)时,系统默认会重新启动当前 Activity。为了在应用或游戏中保留状态,Activity 默认会在重新启动之前调用 onSaveInstanceState()
,并在重新启动之后调用 onRestoreInstanceState()
。但是,此过程要求 Activity 重新加载所有关联的服务和资源。要了解有关此默认行为的更多信息,请参阅处理配置更改指南。
典型的游戏会话会经历多次配置更改。如果您的游戏让系统处理每次配置更改,那么您的游戏场景将反复销毁和重新启动,从而降低游戏性能。因此,我们强烈建议您在游戏中自行处理这些配置更改。
要了解如何将此配置更改逻辑添加到您的游戏,请参阅如何创建自定义配置更改处理程序的部分。
创建灵活的架构
为了使您的游戏支持尽可能多的设备,请遵循以下最佳实践:
- 部署 Android App Bundles 而不是单个 APK。Android App Bundles 允许您将不同分辨率和不同架构模型(如 x86、ARM)的工件打包成一个工件。更好的是,Android App Bundles 支持更大的游戏大小限制;每个基础 APK 可以高达 150 MB,而捆绑包本身可以达到数 GB。
- 添加对 x86 架构的支持。此步骤可提高游戏在不支持 ARM 的设备上的性能,因为这些设备现在无需先翻译即可执行指令。
添加对 Vulkan 的支持
通过支持 Vulkan,您的游戏可以实现更高的图形性能。大多数设备都支持此图形 API。
创建自定义配置更改处理程序
要声明您的游戏自行处理的配置更改类型,请在清单中表示屏幕或复杂界面的每个 <activity>
元素中添加 android:configChanges
属性。
以下代码片段演示了如何声明您的游戏处理屏幕尺寸、屏幕方向和输入法更改:
<activity ... android:configChanges="screenSize|orientation|keyboard|keyboardHidden"> </activity>
当声明的配置更改发生时,系统现在会调用一个不同的方法,onConfigurationChanged()
。在此方法中,添加逻辑以更新游戏 UI:
- 更新屏幕的缩放因子和方向。请记住,出于性能考虑,有时最好仅沿一个维度缩放游戏的 UI。
- 确定玩家使用的最佳输入法。
处理屏幕配置更改
只要您在 android:configChanges
属性中分别包含 screenSize
和 orientation
值,您的游戏就会手动处理屏幕尺寸和屏幕方向更改。您可以使用这些新值来更新场景内容和玩家输入区域。有关如何设计游戏布局以便于更新的指导,请参阅支持不同屏幕尺寸的指南。
在游戏对 onConfigurationChanged()
的实现中,使用传入的 Configuration
对象和窗口管理器的 Display
对象来确定屏幕尺寸和屏幕方向的更新值。
以下代码片段显示了如何获取游戏更新后的屏幕尺寸和方向:
Kotlin
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) val density: Float = resources.displayMetrics.density val newScreenWidthPixels = (newConfig.screenWidthDp * density).toInt() val newScreenHeightPixels = (newConfig.screenHeightDp * density).toInt() // Get general orientation; either Configuration.ORIENTATION_PORTRAIT or // Configuration.ORIENTATION_LANDSCAPE. val newScreenOrientation: Int = newConfig.orientation // Get general rotation; one of: ROTATION_0, ROTATION_90, ROTATION_180, // or ROTATION_270. val newScreenRotation: Int = windowManager.defaultDisplay.rotation }
Java
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); float density = getResources().getDisplayMetrics().density; int newScreenWidthPixels = (int) (newConfig.screenWidthDp * density); int newScreenHeightPixels = (int) (newConfig.screenHeightDp * density); // Get general orientation; either Configuration.ORIENTATION_PORTRAIT or // Configuration.ORIENTATION_LANDSCAPE. int newScreenOrientation = newConfig.orientation; // Get general rotation; one of: ROTATION_0, ROTATION_90, ROTATION_180, // or ROTATION_270. int newScreenRotation = getWindowManager().getDefaultDisplay() .getRotation(); }
请注意,更改可折叠设备的姿态会更改配置,即使您的应用在全屏模式下运行也是如此。因此,如果用户在游戏运行时折叠或展开设备,您的应用可能必须处理屏幕尺寸或像素密度的变化。
游戏专用屏幕特性
以下部分描述了如何根据游戏的特性调整游戏对屏幕尺寸或屏幕方向变化的反应:
全屏模式
在某些平台(例如 ChromeOS)上,Android 应用和游戏默认是窗口化的且可调整大小。如果您的游戏应始终以全屏模式运行,您可以将 android:resizeableActivity
属性设置为 false
,在您的一个 <activity>
元素中,如以下代码片段所示:
<activity ... android:resizeableActivity="false"> </activity>
您还可以将 android:resizeableActivity
属性设置为 false
,以防止发生基于大小的配置更改。但是,除非您的游戏始终以全屏模式运行,否则您应仅将其作为测试目的的临时修复添加。
屏幕方向
如果您的游戏依赖于设备传感器的特定方向,请在您的游戏 Activity 中为 android:screenOrientation
指定一个值,如以下代码片段所示。此设置有助于防止游戏场景意外翻转。
<activity ... android:screenOrientation="landscape"> </activity>
设备特定屏幕质量
以下部分描述了如何根据某些设备具有的特定质量来处理基于屏幕的配置更改。
纵横比
某些设备支持不同的纵横比。例如,可折叠设备在折叠状态下设计为支持 21:9 的纵横比。为了处理这种潜在的纵横比变化,请执行以下至少一项操作:
- 目标 Android 8.0 (API 级别 26) 或更高版本。
- 使您游戏的场景和界面可调整大小。在运行 Android 7.0 (API 级别 24) 及更高版本的设备上,将
android:resizeableActivity
设置为true
。 声明最大支持的纵横比。在与您的游戏关联的
<meta-data>
属性中,将android.max_aspect
设置为2.4
,如以下代码片段所示。但是请记住,大于您指定纵横比的纵横比会导致游戏在显示屏中出现信箱模式。<application> <meta-data android:name="android.max_aspect" android:value="2.4" /> </application>
同时可见的多个活动
许多现代设备支持各种屏幕布局,包括分屏、画中画和大显示区域。使用其中一种布局时,系统可以同时显示多个活动。
在运行 Android 9 (API 级别 28) 或更高版本的设备上,所有顶级可见活动可以同时恢复。然而,为了使此行为正常工作,您的游戏和设备的 OEM 都需要选择加入此功能。您可以通过在游戏的清单中将 android.allow_multiple_resumed_activities
设置为 true
来添加支持,如以下代码片段所示:
<application> <meta-data android:name="android.allow_multiple_resumed_activities" android:value="true" /> </application>
然后,您可以在不同的设备上测试您的游戏,以查看哪些设备提供了多重恢复正常运行所需的 OEM 支持。
有关将游戏配置为多窗口显示一部分的更多信息,请参阅如何添加多窗口支持的指南。
处理不同类型的交互模型
当您在 android:configChanges
属性中分别包含 keyboard
和 keyboardHidden
值时,您的游戏会手动处理键盘存在和键盘可用性。您可以使用这些新值来更新游戏的主要输入法。
在配置游戏以支持多种用户输入类型时,请记住以下几点:
- 检测输入方法而不是单个设备。这种思维方式有助于改善玩家体验,而无需过度关注玩家可能拥有的特定设备。
- 将
keyboardHidden
属性包含在手动处理的配置更改列表中。这样,您的游戏就可以跟踪键盘何时物理连接到设备但无法使用。 确定当前可用的输入方法。为此,请在游戏启动时和每次配置更改后调用
getInputDeviceIds()
。您通常可以根据玩家的首选输入设备来确定他们计划如何与您的游戏互动:
- 玩家通常使用键盘或游戏控制器执行快速按钮序列。
- 玩家通常使用触摸屏或触控板执行更复杂的手势。
- 玩家通常使用鼠标执行更高精度的输入。
以下部分提供了特定类型输入设备的最佳实践。
键盘
为游戏创建键盘布局时,请考虑玩家如何在给定场景中导航以及他们如何与游戏的设置交互。
WASD 键或方向键通常最适合控制角色移动。最好为可控角色在游戏中可以执行的每个重要动作或技能分配一个特定的键。为了最大化玩家体验,请考虑在游戏中添加对自定义按键绑定的支持。
玩家还应该能够使用键盘打开游戏菜单并在其中导航。Esc
键是暂停场景和显示游戏菜单的常见映射。
有关在游戏中支持键盘输入的更多信息,请参阅如何支持键盘导航的指南以及如何处理键盘操作的指南。
游戏手柄
有关在游戏中处理控制器输入的更多信息,请参阅如何支持游戏控制器的指南。
鼠标或触控板
如果您的游戏支持来自鼠标或触控板的玩家输入,请记住玩家与设备的交互方式除了玩游戏之外还有其他方式。重要的是要意识到,通过请求指针捕获,所有鼠标输入都将导向您的游戏。因此,在您的游戏获得所需信息后,释放指针捕获,以便玩家重新获得对其设备的标准鼠标控制。
在运行 Android 8.0 (API 级别 26) 及更高版本的设备上,您可以使用 Mouse Capture API 协助指针捕获过程。在响应高精度输入的游戏中,您可以通过调用 getX()
和 getY()
方法来获取指针的当前坐标。
有关在游戏中添加鼠标输入和触控板输入支持的更多信息,请参阅如何跟踪触摸和指针移动的指南以及如何处理多点触控手势的指南。
测试您的游戏
在发布游戏之前,请通过完成以下部分中描述的步骤来测试游戏对配置更改的响应。
更新您的测试计划
验证游戏功能时,请包含以下测试用例:
- 最小化和最大化包含您的游戏的窗口。(如果您的游戏始终处于全屏模式,则不适用。)
- 更改屏幕尺寸。
- 更改屏幕方向。(如果您的游戏具有固定方向,则不适用。)
- 连接和断开输入设备,例如键盘和鼠标。
- 执行多重恢复(如果您的游戏支持)。
此外,考虑更新您游戏的质量控制系统,以便您可以针对更广泛的玩家体验进行优化。
有关测试游戏的最佳实践,请参阅测试基础指南。
使用测试和调试工具
您可以使用平台支持的各种工具进行测试:
模拟器,包括 Android 模拟器和 Firebase Test Lab。
系统跟踪.
ChromeOS Performance Analyzer,在运行 ChromeOS M75 或更高版本时可用。