GameController API (廃止)

はじめに

Amazon Fire TV 端末では、最大 7 つの Bluetooth コントローラを同時にシステムに接続できます。これにより、マルチプレーヤーのアプリやゲームを開発できます。コントローラを識別して入力イベントを管理するには、InputDevice クラスと標準の Android イベントハンドラ(onKeyUp()onKeyDown()、または onGenericMotionEvent())が含まれた標準の Android メカニズムを使用します。Android のゲームコントローライベント定数については「Amazon Fire ゲームコントローラ入力」を、各種ボタンの管理方法のヒントについては「コントローラ動作のガイドライン」を参照してください。

以前の Fire TV プラットフォームには、ゲームコントローラの入力管理をサポートするカスタム API が含まれていました。Fire OS 5 では、この GameController API が廃止され、将来的にはプラットフォームから削除されます。アプリで GameController API を使用している場合は、代わりに標準の Android(Lollipop、API 22)InputDevice API の使用に移行することを強くお勧めします。

このドキュメントでは、廃止された GameController API から標準の Android 入力端末 API にコードを移行する方法について説明します。

コントローラとプレーヤー番号

GameController API は主に、プレーヤー番号と接続済みコントローラを関連付けるために使用されます。この動作の大半は、標準の Android InputDevice クラスで getControllerNumber() メソッドを使用して実現できます。

各コントローラには、Fire TV に接続されるときに一意の番号が割り当てられます。この番号は、コントローラの接続が切断され、再度接続されると、変更される可能性があります。

リモコンなど、ゲームコントローラ以外の入力端末(具体的には InputDevice.SOURCE_GAMEPADInputDevice.SOURCE_JOYSTICK で指定されていない入力端末)には、コントローラ番号として 0 が割り当てられます。

Amazon GameController API からの移行

Fire OS 5 では、Fire TV GameController API の呼び出しが以前と同じように機能しますが、GameController API は廃止されており、将来的にはプラットフォームから削除されます。現在アプリで GameController API を使用している場合は、標準の Android 入力端末管理を使用するようにコードを移行することを強くお勧めします

GameController API からアプリを移行するには、以下の手順に従います。

  • com.amazon.device.gamecontroller パッケージを含め、GameController クラスに対するすべての参照を削除します。
  • onCreate() メソッドから GameController.init() を削除します。
  • プレーヤー ID を取得するための GameController メソッドを Android InputDevice.getControllerNumber() に置き換えます。
  • GameController イベント定数を、以下の表に示すように、標準の Android KeyEvent 定数または MotionEvent 定数に置き換えます。
GameController 入力定数 Android キーまたはモーションイベント定数
GameController.AXIS_STICK_LEFT_X MotionEvent.AXIS_Y
GameController.AXIS_STICK_LEFT_Y MotionEvent.AXIS_Y
GameController.AXIS_STICK_RIGHT_X MotionEvent.AXIS_Z
GameController.AXIS_STICK_RIGHT_Y MotionEvent.AXIS_RZ
GameController.AXIS_TRIGGER_LEFT
MotionEvent.AXIS_BRAKE
GameController.AXIS_TRIGGER_RIGHT
MotionEvent.AXIS_GAS
GameController.BUTTON_A KeyEvent.KEYCODE_BUTTON_A
GameController.BUTTON_B KeyEvent.KEYCODE_BUTTON_B
GameController.BUTTON_X KeyEvent.KEYCODE_BUTTON_X
GameController.BUTTON_Y KeyEvent.KEYCODE_BUTTON_Y
GameController.BUTTON_SHOULDER_LEFT KeyEvent.KEYCODE_BUTTON_L1
GameController.BUTTON_SHOULDER_RIGHT KeyEvent.KEYCODE_BUTTON_R1
GameController.BUTTON_TRIGGER_LEFT
KeyEvent.KEYCODE_BUTTON_L2
GameController.BUTTON_TRIGGER_RIGHT
KeyEvent.KEYCODE_BUTTON_R2
GameController.BUTTON_STICK_LEFT KeyEvent.KEYCODE_BUTTON_THUMBL
GameController.BUTTON_STICK_RIGHT KeyEvent.KEYCODE_BUTTON_THUMBR
GameController.BUTTON_DPAD_UP
KeyEvent.KEYCODE_DPAD_UP
GameController.BUTTON_DPAD_DOWN KeyEvent.KEYCODE_DPAD_DOWN
GameController.BUTTON_DPAD_LEFT KeyEvent.KEYCODE_DPAD_LEFT
GameController.BUTTON_DPAD_RIGHT KeyEvent.KEYCODE_DPAD_RIGHT
GameController.BUTTON_DPAD_CENTER KeyEvent.KEYCODE_DPAD_CENTER
GameController.BUTTON_AXIS_HAT_X MotionEvent.AXIS_HAT_X
GameController.BUTTON_AXIS_HAT_Y MotionEvent.AXIS_HAT_Y

GameController API のプレーヤー番号(廃止)

1 台の Amazon Fire TV 端末には Bluetooth ゲームコントローラを 7 台まで接続できますが、プレーヤー番号に割り当てられるのはそのうちの 4 台だけです。コントローラはプレーヤー番号スロットに割り当てられるため、個々のコントローラが切断されても採番され直すことはありません。つまり、プレーヤー番号は連番にはなりません。

GameController API には、使用可能なゲームコントローラのプレーヤー番号にアクセスするためのメソッドが含まれています。5 人以上のプレーヤーで遊べるゲームの場合は、ゲーム内のコントローラとプレーヤー番号を自分で管理することもできます。

注意: このセクションで取り上げるメソッドはすべて、GameController.init() を呼び出さずに使用すると、NotInitializedException 例外をスローします。

端末 ID によるプレーヤー番号の取得

GameController.getPlayerNumber() 静的メソッドを Android 端末 ID を指定して呼び出すと、その端末のプレーヤー番号を取得できます。Android 端末 ID は、InputDevice.getDeviceIDs() メソッドを使って取得できます。あるいは、任意のキーイベントまたはモーションイベント (InputEvent.getDeviceId())から、または GameController.getDeviceID() を使ってプレーヤー番号から取得することもできます。

プレーヤー番号はシステム全体で定義されます。一部の端末 ID はプレーヤー番号に関連付けられていないことがあります。端末 ID がプレーヤー番号に関連付けられていない場合、getPlayerNumber()PlayerNumberNotFoundException 例外をスローします。

int[] ids = InputDevice.getDeviceIds();

for (int i=0; i < ids.length; i++) {
try {
int playerNum = GameController.getPlayerNumber(ids[i]);
}   catch (PlayerNumberNotFoundException e) {
// ...
}
// ...
}

プレーヤー番号による端末 ID の取得

GameController.getDeviceID() メソッドは getPlayerNumber() とは逆に、プレーヤー番号(1~4)を指定して呼び出すと Android 端末 ID を返します。一部のプレーヤー番号は端末 ID に関連付けられていないことがあります。プレーヤー番号が端末 ID に関連付けられていない場合、GameController.getDeviceID()DeviceNotFoundException 例外をスローします。

for (int n = 0; n < GameController.MAX_PLAYERS; n++) {
try {
int devicenum = GameController.getDeviceID(n + 1);
}
catch (DeviceNotFoundException e) {
// ...
}
// ...
}

プレーヤー番号の取得

GameController.getPlayerCount() 静的メソッドを使用すると、使用可能なプレーヤー番号を取得できます。プレーヤー番号は必ずしも連番ではない点に注意してください。たとえば、2 人のプレーヤーにスロット 2 と 4 が割り当てられることもあります。

int players = GameController.getPlayerCount();

プレーヤーによる GameController オブジェクトの取得

GameController.getControllerByPlayer() 静的メソッドにプレーヤー番号を指定して呼び出すと、フレームベースのイベント入力に使用する GameController オブジェクトを取得できます(「フレームベースの入力イベント」を参照)。プレーヤー番号は、システムによって 1~4 の範囲で割り当てられます。一部のプレーヤー番号はコントローラに関連付けられていないことがあります。プレーヤー番号がコントローラに関連付けられていない場合、GameController.getControllerbyPlayer()PlayerNumberNotFoundException 例外をスローします。

for (int n = 0; n < GameController.MAX_PLAYERS; n++) {
GameController gameController = null;
try {
gameController =
   GameController.getControllerByPlayer(n + 1);
}
catch (PlayerNumberNotFoundException e) {
// ...
}
}

GameController API のフレームベースの入力イベント(廃止)

入力イベントは、コントローラから標準の Android イベント駆動型プログラミングモデルを使用して処理できます。このモデルでは、入力イベントが到着次第処理されます。GameController クラスでは、フレームベースの入力モデルを使用できます。このモデルでは、イベントがキャッシュされ、各フレームを描画する前にコントローラの状態を問い合わせることができます。このモデルでは、以下の手順を使用します。

  1. 独自のゲームループを用意して、標準の Android イベントモデルを置換します。
  2. 標準の入力イベントを GameController に転送します。
  3. ループ内の各フレームについて、次の処理を実行します。
    • プレーヤー入力に対して、各コントローラの状態を問い合わせます。
    • その入力データを使用してフレームを描画します。

注意: このセクションで取り上げるメソッドはすべて、GameController.init() を呼び出さずに使用すると、NotInitializedException 例外をスローします。

イベントの GameController への転送

GameController API を使用して入力イベントを管理するには、キーイベントとモーションイベントを GameController クラスに転送して処理します。これらのイベントを GameController に渡すように、onKeyUp()onKeyDown()onGenericMotionEvent() の各メソッドを上書きしてください。

//Forward key down events to GameController so it can manage state
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean handled = false;
try {
handled = GameController.onKeyDown(keyCode, event);
}
catch (DeviceNotFoundException e) {
}
return handled || super.onKeyDown(keyCode, event);
}
//Forward key up events to GameController so it can manage state
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
boolean handled = false;
try {
handled = GameController.onKeyUp(keyCode, event);
}
catch (DeviceNotFoundException e) {
}
return handled || super.onKeyUp(keyCode, event);
}
//Forward motion events to GameController so it can manage state
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
boolean handled = false;
try {
handled = GameController.onGenericMotionEvent(event);
}
catch (DeviceNotFoundException e) {
}
return handled || super.onGenericMotionEvent(event);
}

ゲームループの実装

自分のスレッドでゲームループを実装します。そのスレッドの run() メソッド内で、入力をテストして、その入力に基づいて各フレームを描画します。自分のアクティビティの onPause() メソッドと onResume() メソッドで、スレッドを停止また開始するのを忘れないでください。

ループ内で GameController.startFrame() を使用して、フレーム間の入力イベントキューをリセットします。以下に、簡単な例を示します。

while (running){
GameController.startFrame();

//Draw the background

//Draw each player
for (int n = 0; n<GameController.MAX_PLAYERS; n++){
GameController gameController = null;
try {
gameController = GameController.getControllerByPlayer(n + 1);
}
catch (PlayerNumberNotFoundException e) {
}

if (gameController != null){
//Move player n on the board
float x = gameController.getAxisValue(GameController.AXIS_STICK_LEFT_X);
float y = gameController.getAxisValue(GameController.AXIS_STICK_LEFT_Y);

if (gameController.getKeyValue(GameController.BUTTON_TRIGGER_LEFT){
   //Draw laser beam for player n
}
}
}

//Draw the foreground
}

コントローラの状態の問い合わせ

GameController で以下のユーティリティメソッドを使用すると、コントローラの現在の入力状態(軸値やボタンの状態など)を問い合わせることができます。これらのメソッドはすべて GameController オブジェクトに対して呼び出されます。このオブジェクトを取得するには、GameController.getControllerByPlayer() メソッドでプレーヤー番号を指定します。さらに、これらのメソッドはすべて、GameController イベント定数を引数とします。これらの定数の一覧については、「GameController API のイベント定数」を参照してください。

アナログモーション

wasAxisChanged() を使用すると、最後のフレームが描画された後にアナログスティックの移動が発生したかどうかをテストできます。

if (gameController.wasAxisChanged(GameController.AXIS_HAT_X) || gameController.wasAxisChanged(GameController.AXIS_HAT_Y)) {
// ... Dpad navigation
}

最後のフレームの描画以降の実際の値の変化をアナログスティックから取得するには、getAxisValue() を使用します。以下の例では、GameController.DEAD_ZONE 定数の使用方法も示しています。この定数を使用すると、スティックの実際の中心(「デッドゾーン」と呼ばれ、スティックは中心位置にあるが、真の中心軸値が報告されないことがある)について、アナログスティックの移動を正規化できます。

float deltaX = gameController.getAxisValue(GameController.AXIS_STICK_LEFT_X);
float deltaY = gameController.getAxisValue(GameController.AXIS_STICK_LEFT_Y);
if ((deltaX * deltaX + deltaY * deltaY) > GameController.DEAD_ZONE * GameController.DEAD_ZONE){
//stick angle is greater than the center dead zone
x[n] += Math.round(deltaX * 10);
y[n] += Math.round(deltaY * 10);
}

ボタンの押し下げ

wasButtonPressed() メソッドまたは wasButtonReleased() メソッドを使用すると、最後のフレームの描画以降にボタンの入力が発生したかどうかをテストできます。

if (gameController.wasButtonPressed(GameController.BUTTON_A)
|| gameController.wasButtonPressed(GameController.BUTTON_DPAD_CENTER)){
// draw a circle at the current player's x,y position
}

isButtonPressed() を使用すると、任意のボタンの現在の状態をテストできます。

//Dpad button presses, move the player's position:
x[n] += gameController.isButtonPressed(GameController.BUTTON_DPAD_RIGHT) ? +5 : 0;
x[n] += gameController.isButtonPressed(GameController.BUTTON_DPAD_LEFT) ? -5 : 0;
y[n] += gameController.isButtonPressed(GameController.BUTTON_DPAD_DOWN) ? +5 : 0;
y[n] += gameController.isButtonPressed(GameController.BUTTON_DPAD_UP) ? -5 : 0;

GameController API のイベント定数(廃止)

GameController コントローラクラスには、ボタンと軸のイベントタイプを表す静的なイベント定数のセットが含まれています。これらの定数は、KeyEvent クラスと MotionEvent クラスの標準定数と同じですが、コントローラにも同じ定数が定義されています。ただし、定数名は通常、より論理的になっています。たとえば、GameController.BUTTON_AKeyEvent.KEYCODE_BUTTON_A と同じです。これらの定数は、「フレームベースの入力イベント」で説明したフレームベースのコントローラモデルで使用します。

注意: 標準のコントローラボタン([Back]、[Media Play]、[Media FF]、[Media Rewind])の場合、これらのイベントは、標準の Android KeyEvent および MotionEvent イベントを使って管理します。[Home] ボタンと [GameCircle] ボタンはシステムイベントであり、アプリではキャプチャできません。

ゲームコントローラのコントロール アナログ移動:wasAxisChanged() getAxisValue()
ボタンの押し下げ:
wasButtonPressed()
wasButtonReleased()
isButtonPressed()
デフォルトの動作(イベントがコードによって処理されない場合)
D-Pad
(左/右)
AXIS_HAT_X
(正の値は右方向を表します)
なし ユーザーインターフェイスのフォーカスが特定の方向に移動します。
D-Pad
(上/下)
AXIS_HAT_Y
(正の値は下方向を表します)
なし ユーザーインターフェイスのフォーカスが特定の方向に移動します。
左側のジョイスティック
(左/右)
AXIS_STICK_LEFT_X
(正の値は右方向を表します)
なし ユーザーインターフェイスのフォーカスが特定の方向に移動します。
左側のジョイスティック
(上/下)
AXIS_STICK_LEFT_Y
(正の値は下方向を表します)
なし ユーザーインターフェイスのフォーカスが特定の方向に移動します。
左側のジョイスティックの押し下げ なし BUTTON_STICK_LEFT 何も起こりません。
右側のジョイスティック
(左/右)
AXIS_STICK_RIGHT_X
(正の値は右方向を表します)
なし 何も起こりません。
右側のジョイスティック
(上/下)
AXIS_STICK_RIGHT_Y
(正の値は下方向を表します)
なし 何も起こりません。
右側のジョイスティックの押し下げ なし BUTTON_STICK_RIGHT 何も起こりません。
A なし BUTTON_A フォーカスが現在置かれているアイテムが選択されます。
B なし BUTTON_B 前の画面(アクティビティ)に戻ります([Back] と同じ)。
X なし BUTTON_X 何も起こりません。
Y なし BUTTON_Y 何も起こりません。
左側のトリガー(L2) AXIS_TRIGGER_LEFT なし フォーカスが現在置かれているアイテムが選択されます。
左側のショルダー(L1) なし BUTTON_SHOULDER_LEFT 何も起こりません。
右側のトリガー(R2) AXIS_TRIGGER_RIGHT なし フォーカスが現在置かれているアイテムが選択されます。
右側のショルダー(R1) なし BUTTON_SHOULDER_RIGHT 何も起こりません。