支持大屏幕可调整大小

从手机扩展到不同的大屏幕外形规格会引入您的游戏如何处理窗口管理方面的考量。在 ChromeOSGoogle Play 游戏电脑版上,您的游戏可以在主桌面界面上以窗口模式运行。在运行 Android 12L (API 级别 32) 或更高版本且屏幕宽度 > 600dp 的 新 Android 平板电脑和折叠屏设备上,您的游戏可以与其他应用在 分屏模式下并排运行,可以调整大小,甚至可以在 折叠屏设备的内部和外部显示屏之间移动,从而导致窗口尺寸配置更改,在某些设备上还会导致方向更改。

Unity 游戏的可调整大小功能

基本大屏幕配置

声明您的游戏是否能够处理可调整大小

<android:resizeableActivity="true" or "false" />

如果无法支持可调整大小,请确保游戏清单明确定义了支持的最小和最大宽高比

<!-- Render full screen between 3:2 and 21:9 aspect ratio -->
<!-- Let the platform letterbox otherwise -->
<activity android:minAspectRatio="1.5">
<activity android:maxAspectRatio="2.33">

Google Play 游戏电脑版

对于 Google Play 游戏电脑版,平台会处理窗口可调整大小,同时遵守指定的宽高比。窗口大小会自动锁定到最佳尺寸。如果您的主要方向是横向,则需要支持至少 16:9 的宽高比;如果您的游戏是竖向模式,则需要支持 9:16 的宽高比。为了获得最佳体验,对于横向游戏,请明确支持 21:9、16:10 和 3:2 的宽高比。此处不要求窗口可调整大小,但对于其他外形规格兼容性来说仍然是好的。

如需了解更多信息和最佳实践,请参阅配置 Google Play 游戏电脑版的图形。

ChromeOS 和 Android 大屏幕

为了在 ChromeOS 和大屏幕 Android 设备上最大化您的游戏在全屏模式下的可见区域,请支持全屏沉浸模式并通过在 decorView、系统 UI 可见性或通过 WindowInsetsCompat API 设置标志来隐藏系统栏。您还需要优雅地处理旋转和调整大小的配置事件,或者阻止它们在 ChromeOS 设备上发生。

请注意,在大屏幕 Android 设备上,您的游戏可能会在您尚未处理的配置中运行。如果您的游戏不支持所有窗口尺寸和方向配置,平台会以兼容模式将您的游戏进行信箱模式显示,并且在必要时,会在更改为不受支持的配置之前提示玩家。

图 1. 配置兼容性对话框。

在某些设备上,当玩家移动到不受支持的配置时,可能会提示他们选择重新加载游戏并重新创建活动,以最适合新的窗口布局,这会中断游戏体验。在各种多窗口模式配置(2/3、1/2、1/3 窗口大小)中测试您的游戏,并验证没有游戏玩法或 UI 元素被切断或无法访问。此外,测试您的游戏在可折叠设备上在内部和外部屏幕之间移动时如何响应可折叠连续性。如果您发现问题,请明确处理这些配置事件并添加高级大屏幕可调整大小支持。

高级大屏幕可调整大小

图 2. 桌面和台式模式折叠屏上的不同 UI。

要退出兼容模式并避免活动重新创建,请执行以下操作:

  1. 声明您的主活动可调整大小

    <android:resizeableActivity="true" />
    
  2. 在游戏清单的 <activity> 元素的 android:configChanges 属性中声明对“orientation”、“screenSize”、“smallestScreenSize”、“screenLayout”和“density”的显式支持,以接收所有大屏幕配置事件

    <android:configChanges="screenSize | smallestScreenSize | screenLayout | orientation | keyboard |
                            keyboardHidden | density" />
    
  3. 重写 onConfigurationChanged() 并处理配置事件,包括当前方向、窗口大小、宽度和高度

    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()
    
       // Configuration.ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE
       val newScreenOrientation: Int = newConfig.orientation
    
       // 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);
    
       // Configuration.ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE
       int newScreenOrientation = newConfig.orientation;
    
       // ROTATION_0, ROTATION_90, ROTATION_180, or ROTATION_270
       int newScreenRotation = getWindowManager().getDefaultDisplay()
               .getRotation();
    }

您还可以查询 WindowManager 以检查当前设备旋转。使用此元数据,检查新的窗口尺寸并渲染到全窗口大小。由于宽高比差异,这在所有情况下可能不起作用,因此,您可以选择将游戏 UI 固定到新的窗口大小,并将核心游戏内容进行信箱模式显示。如果存在技术或设计限制阻止其中任何一种方法,请在引擎内部执行信箱模式显示以保持宽高比,并缩放到最佳尺寸,同时声明 resizeableActivity = false 并避免配置模式。

无论您采用何种方法,请在各种配置下测试您的游戏(折叠和展开、不同的旋转变化、分屏模式),并确保没有被切断或重叠的游戏内 UI 元素、触摸目标可访问性问题或导致游戏拉伸、挤压或以其他方式变形的宽高比问题。

此外,更大的屏幕通常意味着更大的像素,因为在更大的区域内拥有相同数量的像素。这可能会导致缩小渲染缓冲区或较低分辨率资产的像素化。在大屏幕设备上使用最高质量的资产,并对您的游戏进行性能分析,以确保没有问题。如果您的游戏支持多个质量级别,请确保它考虑了大屏幕设备。

多窗口模式

多窗口模式使多个应用能够同时共享同一屏幕。多窗口模式不会改变活动生命周期;但是,多个窗口中应用的恢复状态在不同版本的 Android 上有所不同(请参阅支持多窗口模式中的多窗口模式下的活动生命周期)。

当玩家将应用或游戏置于多窗口模式时,系统会通知活动配置更改,如高级大屏幕可调整大小部分所述。当玩家调整游戏大小或将游戏恢复到全屏模式时,也会发生配置更改。

无法保证应用在进入多窗口模式时会重新获得焦点。因此,如果您使用任何应用状态事件来暂停游戏,请不要依赖于获取焦点事件(onWindowFocusChanged() 且焦点值为 true)来恢复游戏。相反,请使用其他事件处理程序或状态更改处理程序,例如 onConfigurationChanged()onResume()。请注意,您始终可以使用 isInMultiWindowMode() 方法来检测当前活动是否在多窗口模式下运行。

在 ChromeOS 的多窗口模式下,初始窗口尺寸成为一个重要的考虑因素。游戏不必是全屏的,您需要声明在这种情况下窗口的尺寸应该是什么。有两种推荐的方法来处理这个问题。

第一种方法通过在 Android 清单中的 <layout> 标签上使用特定属性来实现。defaultHeightdefaultWidth 属性控制初始尺寸。同时还要注意 minHeightminWidth 属性,以防止您的玩家将游戏窗口调整到您不支持的尺寸。最后,还有 gravity 属性,它决定了窗口启动时在屏幕上出现的位置。以下是一个使用这些属性的布局标签示例:

<layout android:defaultHeight="500dp"
        android:defaultWidth="600dp"
        android:gravity="top|end"
        android:minHeight="450dp"
        android:minWidth="300dp" />

设置窗口大小的第二种方法是通过使用动态启动边界。通过使用 setLaunchBounds(Rect)⁠⁠,您可以定义起始窗口尺寸。如果指定空矩形,则活动将以最大化状态启动。

此外,如果您使用的是 Unity 或 Unreal 游戏引擎,请确保您使用的是最新版本(Unity 2019.4.40 及更高版本,Unreal 5.3 或更高版本),它们对多窗口模式提供了良好的支持。

折叠屏姿态支持

使用 Jetpack WindowManager 布局库支持折叠屏姿态(例如台式模式),以增加玩家的沉浸感和参与度

图 3. 游戏处于台式模式,主视图位于显示屏的垂直部分,控件位于水平部分。

Kotlin

fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
}

Java

boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}