スマートフォンからさまざまな大画面フォーム ファクタに拡大すると、ゲームでのウィンドウ管理の処理方法を考慮する必要があります。Googlebook、ChromeOS、PC 版 Google Play Games では、ゲームはメインのデスクトップ インターフェース上にウィンドウ モードで実行できます。画面幅が 600 dp を超える Android 12L(API レベル 32)以上を搭載した新しい Android タブレットと折りたたみ式デバイスでは、ゲームは他のアプリと並べて分割画面モードで実行でき、サイズ変更も可能です。折りたたみ式デバイスでは、内側と外側のディスプレイ間で移動することもできます。これにより、ウィンドウ サイズと、一部のデバイスでは向きの設定が変更されます。
基本的な大画面構成
ゲームがサイズ変更に対応できるかどうかを宣言します。
<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">
PC 版 Google Play Games
PC 版 Google Play Games では、指定されたアスペクト比を維持しながら、プラットフォームがウィンドウのサイズ変更を処理します。ウィンドウ サイズは最適なサイズに自動的にロックされます。主な画面の向きが横向きの場合は 16:9 以上のアスペクト比、縦向きモードのゲームの場合は 9:16 のアスペクト比をサポートする必要があります。最適なエクスペリエンスを実現するには、横向きゲームで 21:9、16:10、3:2 のアスペクト比を明示的にサポートします。ここではウィンドウのサイズ変更は必須ではありませんが、他のフォーム ファクタとの互換性を保つために、サイズ変更可能にしておくことをおすすめします。
詳細とベスト プラクティスについては、PC 版 Google Play Games のグラフィックを設定するをご覧ください。
大画面
大画面タブレット、折りたたみ式デバイス、Googlebook、ChromeOS、接続されたディスプレイでゲームの表示領域を最大化するには、全画面イマーシブ モードをサポートし、decorView、システム UI の可視性、または WindowInsetsCompat API でフラグを設定してシステムバーを非表示にします。また、回転やサイズ変更の構成イベントを適切に処理するか、発生を防ぐことも必要です。
大画面の Android デバイスでは、ゲームはまだ対応していない構成で実行される可能性があります。ゲームがすべてのウィンドウ サイズと画面の向きの構成をサポートしていない場合、プラットフォームはゲームを互換モードでレターボックス表示し、必要に応じて、サポートされていない構成に変更する前にプレーヤーに確認します。
一部のデバイスでは、プレーヤーがサポートされていない構成に移動すると、新しいウィンドウ レイアウトに最適なようにゲームを再読み込みしてアクティビティを再作成するオプションが表示されることがあります。これにより、プレイ体験が中断されます。さまざまなマルチ ウィンドウ モードの構成(ウィンドウ サイズ 2/3、1/2、1/3)でゲームをテストし、ゲームプレイや UI 要素が切り取られたり、アクセスできなくなったりしないことを確認します。また、折りたたみ式デバイスのインナー スクリーンとアウター スクリーンを切り替える際に、ゲームが折りたたみ式デバイスの継続性に対応しているかどうかもテストします。問題が見つかった場合は、これらの構成イベントを明示的に処理し、大画面の高度なサイズ変更サポートを追加します。
大画面の高度なサイズ変更
互換モードを終了してアクティビティの再作成を回避するには、次の操作を行います。
メイン アクティビティをサイズ変更可能として宣言します。
<android:resizeableActivity="true" />ゲーム マニフェストの
<activity>要素のandroid:configChanges属性で、「orientation」、「screenSize」、「smallestScreenSize」、「screenLayout」、「density」のサポートを明示的に宣言して、大画面の画面構成イベントをすべて受け取ります。<android:configChanges="screenSize | smallestScreenSize | screenLayout | orientation | keyboard | keyboardHidden | density" />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 のバージョンによって異なります(マルチ ウィンドウ モードをサポートするのマルチ ウィンドウ モードでのアクティビティのライフサイクルをご覧ください)。
プレーヤーがアプリやゲームをマルチ ウィンドウ モードにすると、システムは 大画面の高度なサイズ変更機能セクションで指定されているとおりに、構成の変更をアクティビティに通知します。構成の変更は、プレーヤーがゲームのサイズを変更したり、ゲームを全画面モードに戻したりした場合にも発生します。
マルチ ウィンドウ モードに切り替えたときにアプリがフォーカスを取り戻す保証はありません。そのため、アプリの状態イベントを使用してゲームを一時停止する場合は、ゲームを再開するためにフォーカス取得イベント(フォーカス値が true の onWindowFocusChanged())に依存しないでください。代わりに、onConfigurationChanged() や onResume() などの他のイベント ハンドラまたは状態変更ハンドラを使用してください。なお、isInMultiWindowMode() メソッドを使用すると、現在のアクティビティがマルチ ウィンドウ モードで実行されているかどうかを常に検出できます。
Googlebook、ChromeOS、大画面タブレット、折りたたみ式デバイスのデスクトップ ウィンドウでは、初期ウィンドウのサイズが重要な考慮事項になります。ゲームは全画面表示である必要はありません。その場合、ウィンドウのサイズを宣言する必要があります。これには、次の 2 つの方法をおすすめします。
1 つ目の方法は、Android マニフェストの <layout> タグで特定の属性を使用することで機能します。defaultHeight 属性と defaultWidth 属性は、初期のディメンションを制御します。また、minHeight 属性と minWidth 属性にも注意して、プレーヤーがサポートされていないサイズにゲーム ウィンドウのサイズを変更できないようにしてください。最後に、gravity 属性があります。これは、起動時にウィンドウが画面上のどこに表示されるかを決定します。これらの属性を使用したレイアウトタグの例を次に示します。
<layout android:defaultHeight="500dp"
android:defaultWidth="600dp"
android:gravity="top|end"
android:minHeight="450dp"
android:minWidth="300dp" />
ウィンドウ サイズを設定する 2 つ目の方法は、動的起動境界を使用する方法です。setLaunchBounds(Rect) を使用すると、開始ウィンドウのサイズを定義できます。空の長方形が指定されている場合、アクティビティは最大化された状態で開始されます。
また、Unity または Unreal ゲームエンジンを使用している場合は、マルチウィンドウ モードを適切にサポートする最新バージョン(Unity 2019.4.40 以降、Unreal 5.3 以降)を使用していることを確認してください。
折りたたみ式デバイスの形状のサポート
Jetpack WindowManager レイアウト ライブラリを使用して、テーブルトップなどの折りたたみ式デバイスのポスチャーをサポートし、プレーヤーの没入感とエンゲージメントを高めます。
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); }