Android 向けのゲームを開発する場合、考えられるさまざまなプレーヤー エクスペリエンスを予測し、プレーヤーのリアルタイム インタラクションに対する要求に適応できるようにすることが重要です。さまざまなプレーヤー エクスペリエンスをサポートすることにより、ゲームプレイの柔軟性が向上し、より多くのユーザーにアプローチできるようになります。
プレーヤー エクスペリエンスの具体的な違いには、次のようなものがあります。
- デバイスのフォーム ファクタ: スマートフォンでは従来の Android デバイスのエクスペリエンスを適用しますが、他のフォーム ファクタでもゲームをプレイできます。ChromeOS デバイスでは、ゲームを表示する Android コンテナを実行できます。Android を実行できるタブレットでは、複数のレベルの精細度がサポートされます。Android TV デバイスは、より高精細な没入型のエクスペリエンスをサポートします。ディスプレイ拡張ツールを使用してマルチウィンドウ環境をシミュレートできます。折りたたみ式デバイスを使用している場合は、ゲームプレイ セッション中に画面のサイズを変更できます。
- 操作方法: デバイスの画面にタッチして操作できますが、マウス、タッチパッド、キーボード、コントローラでも操作できます。さらに、ディスプレイ拡張ツールと折りたたみ式デバイスにより、より大きな画面でゲームを体験でき、より長いゲームプレイ セッションと複雑なインターフェースが利用できるようになります。
- ハードウェアのサポート: 一部の Android 搭載デバイスには、ハンドヘルド デバイスでは一般的な、背面カメラ、GPS、ネットワーク接続などのハードウェアがありません。ゲームでは、使用できるハードウェアに対応し、特定の機能が使用できない状況に適切に対応する必要があります。
このガイドでは、さまざまな種類の画面とユーザー操作に向けたゲーム開発に関するおすすめの方法を紹介します。また、ゲームの設計と効果的なテスト戦略の開発に関する提案も提供します。
ゲーム設計のおすすめの方法
ゲームの設計とアーキテクチャを計画するときは、次のセクションで説明するおすすめの方法に従ってください。
構成変更に手動で対応する
Android システムは、画面サイズの変更、画面の向きの変更、入力方法の変更などの構成変更を検出したとき、デフォルトでは現在のアクティビティを再起動します。アプリ内またはゲーム内の状態を保持するために、アクティビティはデフォルトで再起動前に onSaveInstanceState()
を呼び出し、再起動後に onRestoreInstanceState()
を呼び出します。ただし、このプロセスでは、関連するサービスとリソースをすべてアクティビティがリロードする必要があります。このデフォルトの動作の詳細については、構成変更の処理についてのガイドをご覧ください。
通常のゲームプレイ セッションでは、いくつかの構成が変更されます。システムに構成変更の処理を任せると、構成変更のたびにゲームのシーンが破棄されて再起動が発生し、ゲームのパフォーマンスが低下します。このため、ゲーム内での構成変更はご自身で処理することを強くおすすめします。
この構成変更のロジックをゲームに追加する方法については、カスタム構成変更ハンドラの作成方法のセクションをご覧ください。
柔軟なアーキテクチャを作成する
できるだけ多くのデバイスをゲームでサポートするため、次のおすすめの方法に従ってください。
- 個別の APK の代わりに Android App Bundle をデプロイする。Android App Bundle を使用すると、複数の解像度やアーキテクチャ モデル(x86、ARM など)のアーティファクトを 1 つのアーティファクトにパッケージ化できます。そのうえ、Android App Bundles ではゲームのサイズ制限が緩和され、各ベース APK は 150 MB まで、バンドル自体は数ギガバイトまで許されます。
- x86 アーキテクチャのサポートを追加する。これにより、ARM をサポートしていないデバイスでゲームのパフォーマンスが向上します。これは、実行する前にデバイスで命令を変換する必要がなくなるためです。
Vulkan のサポートを追加する
Vulkan のサポートにより、ゲームのグラフィック パフォーマンスが向上します。ほとんどのデバイスがこのグラフィック API をサポートしています。
カスタム構成変更ハンドラの作成
ゲーム自体が処理する構成変更のタイプを宣言するには、マニフェストにおいて画面または複雑なインターフェースを表す各 <activity>
要素に android:configChanges
属性を追加します。
次のコード スニペットでは、画面サイズ、画面の向き、入力方法の変更をゲームで処理することを宣言しています。
<activity ... android:configChanges="screenSize|orientation|keyboard|keyboardHidden"> </activity>
宣言された構成の変更が発生すると、システムにより別のメソッド onConfigurationChanged()
が呼び出されます。このメソッドに、次のようなゲームの UI を更新するロジックを追加します。
- 画面のスケール係数と向きを更新する。パフォーマンスのためには、ゲームの UI を 1 つのディメンションに絞って拡大縮小するほうが良い場合があることに留意してください。
- プレーヤーが使用する最適な入力方法を見つける。
画面構成の変更を処理する
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 のアプリやゲームをウィンドウ処理して、
デフォルトでサイズ変更可能です。ゲームを常に全画面モードで実行する必要がある場合は、<activity>
要素のいずれかを使用して android:resizeableActivity
属性を false
に設定できます。次のコード スニペットをご覧ください。
<activity ... android:resizeableActivity="false"> </activity>
また、android:resizeableActivity
属性を false
に設定して、サイズに基づく構成変更が発生しないようにもできます。ただし、ゲームを常に全画面モードで実行させるのでなければ、この属性の追加はテスト目的の一時的な変更に留める必要があります。
画面の向き
ゲームの画面の向きがデバイスのセンサーに依存している場合、次のコード スニペットのように、ゲームのアクティビティで 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 Emulator、Firebase Test Lab など)
ChromeOS のパフォーマンス Analyzer、 使用できます。