支持大屏幕尺寸调整

从手机扩展到不同类型的大屏设备时,需要考虑游戏如何处理窗口管理。在 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 Games 电脑版

对于 Google Play 游戏电脑版,该平台会在遵循指定宽高比的情况下处理窗口大小可调整性。窗口大小会自动锁定到最佳尺寸。如果您的主要屏幕方向为横向,则至少需要支持 16:9 的宽高比;如果您的游戏为竖屏模式,则至少需要支持 9:16 的宽高比。为了获得最佳体验,请为横向游戏明确支持 21:9、16:10 和 3:2 的宽高比。此处不需要窗口可调整大小,但为了实现其他设备规格兼容性,最好还是提供此功能。

如需了解详情和最佳实践,请参阅针对 Google Play 游戏电脑版配置图形功能

ChromeOS 和 Android 大屏设备

如需在 ChromeOS 和大屏 Android 设备上最大限度地扩大游戏的全屏可视区域,请支持全屏沉浸式模式,并通过在 decorView 上设置标志、系统界面可见性或通过 WindowInsetsCompat API 隐藏系统栏。您还需要妥善处理旋转和调整大小配置事件,或防止在 ChromeOS 设备上发生此类事件。

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

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

在某些设备上,当玩家移至不受支持的配置时,系统可能会提示他们重新加载游戏并重新创建 activity,以便最佳地适应新的窗口布局,这会干扰游戏体验。在各种多窗口模式配置(2/3、1/2、1/3 窗口大小)中测试您的游戏,并验证游戏内容或界面元素是否会被截断或无法访问。此外,请测试您的游戏在可折叠设备的内屏和外屏之间移动时如何响应可折叠设备的连续性。如果您发现问题,请明确处理这些配置事件,并添加对大屏幕的增强型大小调整支持。

高级大屏设备大小调整

图 2. 桌面设备和处于桌面折叠状态的可折叠设备上的不同界面。

如需退出兼容模式并避免重新创建 activity,请执行以下操作:

  1. 将主 activity 声明为可调整大小:

    <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 以检查当前的设备旋转状态。使用此元数据,检查新的窗口尺寸并渲染到完整的窗口大小。由于宽高比不同,这种方法可能并不适用于所有情况。因此,您也可以将游戏界面固定到新的窗口大小,并将核心游戏内容以信箱模式显示。如果技术或设计限制阻止您使用上述任一方法,请自行在引擎中实现信箱模式以保留宽高比,并在声明 resizeableActivity = false 并避免配置模式时缩放到最佳尺寸。

无论您采用哪种方法,请在各种配置下测试游戏(折叠和展开、不同的旋转更改、分屏模式),并确保游戏界面元素不会被截断或重叠,触摸目标可单手操作,并且宽高比问题不会导致游戏画面被拉伸、压缩或以其他方式失真。

此外,屏幕越大,像素通常也越大,因为要在更大的屏幕中显示相同数量的像素。当使用缩小的渲染缓冲区或分辨率较低的素材资源时,这可能会导致像素化问题。在大屏设备上使用优质资源,并对游戏进行性能分析,确保没有任何问题。如果您的游戏支持多种画质级别,请确保它适用于大屏设备。

多窗口模式

多窗口模式允许多个应用同时共享同一屏幕。多窗口模式不会更改 activity 生命周期;不过,在不同版本的 Android 系统中,应用在多窗口中的恢复状态会有所不同(请参阅支持多窗口模式中的多窗口模式下的 activity 生命周期)。

大屏设备上的高级大小调整部分中所述,当玩家将应用或游戏置于多窗口模式下时,系统会通知 activity 发生的配置更改。当玩家调整游戏大小或将游戏恢复为全屏模式时,也会发生配置更改。

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

在 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)⁠⁠ 定义起始窗口尺寸。如果指定一个空矩形,activity 将以最大化状态启动。

此外,如果您使用的是 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);
}