このトピックでは、入力変換モードが理想的なプレーヤー エクスペリエンスを提供しないゲームで PC 版 Google Play Games のマウス入力を実装する方法について説明します。
PC のプレーヤーは通常、タッチスクリーンではなくキーボードとマウスを利用するため、ゲームがマウス入力に対応するかどうかを検討することが大切です。デフォルトでは、PC 版 Google Play Games は左クリックのマウスイベントをシングル バーチャル タップイベントに変換します。これを「入力変換モード」と呼びます。
このモードの場合、ゲームを動作させるにはわずかな変更で済みますが、PC のプレーヤーにはネイティブ感のエクスペリエンスが提供されません。そのため、以下を実装することをおすすめします。
- コンテキスト メニューには、長押しアクションではなくホバー状態を実装する
- 長押しやコンテキスト メニューで生じる代替アクションには右クリックを実装する
- ファースト パーソン アクション ゲームやサードパーソン アクション ゲームには、押してドラッグのイベントではなく、マウスルックを実装する
PC で一般的な UI パターンをサポートするためには、入力変換モードを無効にする必要があります。
PC 版 Google Play Games の入力処理は、ChromeOS の場合と同じです。PC をサポートする変更を加えると、すべての Android プレーヤーを対象とするゲームも改善されます。
入力変換モードを無効にする
AndroidManifest.xml
ファイルで、android.hardware.type.pc
機能を宣言します。これは、ゲームが PC ハードウェアを使用し、入力変換モードを無効にすることを示します。さらに、required="false"
を追加すると、マウスのないスマートフォンやタブレットにもゲームをインストールできます。次に例を示します。
<manifest ...>
<uses-feature
android:name="android.hardware.type.pc"
android:required="false" />
...
</manifest>
製品版の PC 版 Google Play Games は、ゲームの起動時に正しいモードに切り替わります。デベロッパー エミュレータで実行する場合は、タスクバー アイコンを右クリックし、[Developer Options]、[PC モード(KiwiMouse)] の順に選択して未加工のマウス入力を受け取る必要があります。
この操作を行うと、View.onGenericMotionEvent によってマウスの移動が報告され、ソース SOURCE_MOUSE
でマウスイベントであることが示されます。
Kotlin
gameView.setOnGenericMotionListener { _, motionEvent -> var handled = false if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { // handle the mouse event here handled = true } handled }
Java
gameView.setOnGenericMotionListener((view, motionEvent) -> { if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { // handle the mouse event here return true; } return false; });
マウス入力の処理方法について詳しくは、Chrome OS のドキュメントをご覧ください。
マウス移動の処理
マウスの移動を検出するには、ACTION_HOVER_ENTER
、ACTION_HOVER_EXIT
、ACTION_HOVER_MOVE
のイベントをリッスンします。
これは、ユーザーがゲーム内のボタンやオブジェクトにカーソルを合わせたことを検出するのにおすすめの方法で、ヒントボックスを表示したり、マウスオーバー状態を実装してプレーヤーが選択しようとしている項目をハイライト表示したりするのに役立ちます。次に例を示します。
Kotlin
gameView.setOnGenericMotionListener { _, motionEvent -> var handled = false if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { when(motionEvent.action) { MotionEvent.ACTION_HOVER_ENTER -> Log.d("MA", "Mouse entered at ${motionEvent.x}, ${motionEvent.y}") MotionEvent.ACTION_HOVER_EXIT -> Log.d("MA", "Mouse exited at ${motionEvent.x}, ${motionEvent.y}") MotionEvent.ACTION_HOVER_MOVE -> Log.d("MA", "Mouse hovered at ${motionEvent.x}, ${motionEvent.y}") } handled = true } handled }
Java
gameView.setOnGenericMotionListener((view, motionEvent) -> { if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_HOVER_ENTER: Log.d("MA", "Mouse entered at " + motionEvent.getX() + ", " + motionEvent.getY()); break; case MotionEvent.ACTION_HOVER_EXIT: Log.d("MA", "Mouse exited at " + motionEvent.getX() + ", " + motionEvent.getY()); break; case MotionEvent.ACTION_HOVER_MOVE: Log.d("MA", "Mouse hovered at " + motionEvent.getX() + ", " + motionEvent.getY()); break; } return true; } return false; });
マウスボタンの処理
PC には昔から左右両方のマウスボタンが搭載され、インタラクティブな要素にプライマリ アクションとセカンダリ アクションの両方が割り当てられます。ゲームでは、ボタンのタップなどのタップ アクションは、左クリックにマッピングするのが最適で、長押しアクションは、右クリックで行うのが最も自然です。リアルタイム戦略ゲームでは、左クリックで選択し、右クリックで移動することもできます。ファースト パーソン シューティング ゲームでは、プライマリ ファイアを左クリック、セカンダリ ファイアを右クリックに割り当てることができます。エンドレス ランナーでは、左クリックでジャンプ、右クリックでダッシュを行えます。ミドルクリック イベントのサポートは追加されていません。
ボタンの押下を処理するには、ACTION_DOWN
と ACTION_UP
を使用します。次に、getActionButton
を使用してアクションをトリガーしたボタンを特定するか、getButtonState
を使用してすべてのボタンの状態を取得します。
この例では、列挙型を使用して getActionButton
の結果を表示します。
Kotlin
enum class MouseButton { LEFT, RIGHT, UNKNOWN; companion object { fun fromMotionEvent(motionEvent: MotionEvent): MouseButton { return when (motionEvent.actionButton) { MotionEvent.BUTTON_PRIMARY -> LEFT MotionEvent.BUTTON_SECONDARY -> RIGHT else -> UNKNOWN } } } }
Java
enum MouseButton { LEFT, RIGHT, MIDDLE, UNKNOWN; static MouseButton fromMotionEvent(MotionEvent motionEvent) { switch (motionEvent.getActionButton()) { case MotionEvent.BUTTON_PRIMARY: return MouseButton.LEFT; case MotionEvent.BUTTON_SECONDARY: return MouseButton.RIGHT; default: return MouseButton.UNKNOWN; } } }
この例では、アクションはホバーイベントと同じように処理されます。
Kotlin
// Handle the generic motion event gameView.setOnGenericMotionListener { _, motionEvent -> var handled = false if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { when (motionEvent.action) { MotionEvent.ACTION_BUTTON_PRESS -> Log.d( "MA", "${MouseButton.fromMotionEvent(motionEvent)} pressed at ${motionEvent.x}, ${motionEvent.y}" ) MotionEvent.ACTION_BUTTON_RELEASE -> Log.d( "MA", "${MouseButton.fromMotionEvent(motionEvent)} released at ${motionEvent.x}, ${motionEvent.y}" ) } handled = true } handled }
Java
gameView.setOnGenericMotionListener((view, motionEvent) -> { if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_BUTTON_PRESS: Log.d("MA", MouseButton.fromMotionEvent(motionEvent) + " pressed at " + motionEvent.getX() + ", " + motionEvent.getY()); break; case MotionEvent.ACTION_BUTTON_RELEASE: Log.d("MA", MouseButton.fromMotionEvent(motionEvent) + " released at " + motionEvent.getX() + ", " + motionEvent.getY()); break; } return true; } return false; });
マウスホイール スクロールの処理
ゲーム内のスクロール エリアには、ピンチしてズームの操作やタップしてドラッグの代わりにマウス スクロール ホイールを使用することをおすすめします。
スクロール ホイールの値を読み取るには、ACTION_SCROLL
イベントをリッスンします。最後のフレーム以降のデルタは、getAxisValue
を使用し、垂直オフセットには AXIS_VSCROLL
、水平オフセットには AXIS_HSCROLL
を指定して取得できます。次に例を示します。
Kotlin
gameView.setOnGenericMotionListener { _, motionEvent -> var handled = false if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { when (motionEvent.action) { MotionEvent.ACTION_SCROLL -> { val scrollX = motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL) val scrollY = motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL) Log.d("MA", "Mouse scrolled $scrollX, $scrollY") } } handled = true } handled }
Java
gameView.setOnGenericMotionListener((view, motionEvent) -> { if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_SCROLL: float scrollX = motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL); float scrollY = motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL); Log.d("MA", "Mouse scrolled " + scrollX + ", " + scrollY); break; } return true; } return false; });
マウス入力のキャプチャ
マウスの移動をカメラの移動にマッピングするファースト パーソン アクション ゲームやサードパーソン アクション ゲームなど、一部のゲームではマウスカーソルを完全に制御する必要があります。マウスの排他制御には、View.requestPointerCapture()
を呼び出します。
requestPointerCapture()
は、ビューを含むビュー階層にフォーカスがある場合にのみ動作します。このため、onCreate
コールバックでポインタのキャプチャを取得することはできません。メインメニューを操作するときなど、プレーヤーの操作によってマウスポインタがキャプチャされるまで待つか、onWindowFocusChanged
コールバックを使用する必要があります。次に例を示します。
Kotlin
override fun onWindowFocusChanged(hasFocus: Boolean) { super.onWindowFocusChanged(hasFocus) if (hasFocus) { gameView.requestPointerCapture() } }
Java
@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { View gameView = findViewById(R.id.game_view); gameView.requestPointerCapture(); } }
requestPointerCapture()
によってキャプチャされたイベントは、OnCapturedPointerListener
を登録したフォーカス可能なビューにディスパッチされます。次に例を示します。
Kotlin
gameView.focusable = View.FOCUSABLE gameView.setOnCapturedPointerListener { _, motionEvent -> Log.d("MA", "${motionEvent.x}, ${motionEvent.y}, ${motionEvent.actionButton}") true }
Java
gameView.setFocusable(true); gameView.setOnCapturedPointerListener((view, motionEvent) -> { Log.d("MA", motionEvent.getX() + ", " + motionEvent.getY() + ", " + motionEvent.getActionButton()); return true; });
プレーヤーが一時停止メニューを操作できるようにするなど、排他的なマウス キャプチャを解放するためには、View.releasePointerCapture()
を呼び出します。