Nexus Qで動くプログラムの開発第三回です。NexusQこわれちゃったのでもう開発できませんし、NexusQのページも消えちゃったし、いまさら感なのですが、最終回です。

イベントの取得と通知

Nexus Q を入力端末としてゲーム開発をするためには、他の Android 端末と Nexus Q の違いを把握しておく必要があります。
一番大きな違いであり、同時に Nexus Q の制約になっているものは、イベントの取得です。
Nexus Q では、次の3つのキーイベントが取得できます。

  • KEYCODE_VOLUME_UP
  • KEYCODE_VOLUME_DOWN
  • KEYCODE_VOLUME_MUTE

次に、Unity で取得できるキーコードの一覧はこちらです。
Back キーを取得するのであれば、Unity 側で Input.GetKey(KeyCode.Escape) の値を取れば良いだけなので簡単ですが、リンク先のキーコード一覧を見たところ、ボリュームを取得するキーコードはないので、Android 側から Unity 側にキーイベントを確認させる仕組みを作る必要があります。

Artemis では、キーイベントを取得するために、UnityPlayerActivity を継承した Activity を使っています。
そこでの、キーイベントは次のコードに示すような形式で取得しています。


private static final boolean IS_NEXUS_Q;
private static boolean isTap = false;
private boolean tapCheck = false;
static {
IS_NEXUS_Q = Build.MODEL.equals("Nexus Q");
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (!IS_NEXUS_Q)
return super.onKeyDown(keyCode, event);
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_DOWN:		// 左回り
break;
case KeyEvent.KEYCODE_VOLUME_UP:		// 右回り
break;
case KeyEvent.KEYCODE_VOLUME_MUTE:		// タップ
if (!tapCheck) {
tapCheck = true;
isTap = true;
return true;
}
return super.onKeyDown(keyCode, event);
default:
break;
}
return true;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (!IS_NEXUS_Q)
return super.onKeyUp(keyCode, event);
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_MUTE:		// タップ
tapCheck = false;
break;
default:
break;
}
return true;
}

キーイベントは、他の Android 端末でも、Back キーを押下したときにも呼び出されるので、Nexus Q かどうかで処理を分岐する必要があります。
そのため、onKeyDown と onKeyUp の最初で、Nexus Q であるかどうかを確認しています。

VOLUME_UP と VOLUME_DOWN は、Nexus Q をぐりぐり回すと、onKeyDown と onKeyUp のイベントが2つセットでほぼ同時に連続して発生します。
これは、フライトシミュレータなどのゲームではスロットル操作に使えると思います。

VOLUME_MUTE は、Nexus Q を押しっぱなしにすると、onKeyDown が約19回/秒で発生します。
そして、手を離したときに onKeyUp が1回だけ発生します。
そこで、Nexus Q を1回押すだけで、onKeyDown のイベントが1回だけ発生するように動作させるために、tapCheckとisTap という変数を設けました。
isTap は最初の onKeyDown で true になるので、Unity 側で、isTap の状態をチェックしてあげます。
Unity 側からは、Plugins フォルダに作った C# に次のコードを組み込んで、Android ネイティブのメソッドを呼び出せるようにしておきます。


public class AndroidBehavior : MonoBehaviour {
// タップされたかどうか
public static bool isTapped() {
AndroidJavaClass nexusQActivity = new AndroidJavaClass("jp.co.taosoftware.android.artemis.NexusQActivity");
return nexusQActivity.CallStatic<bool>("isTapped");
}
}

この例で呼び出されるメソッドは NexusQActivity に作った次のようなメソッドです。


/**
* Unity からタップされたかどうか確認するために呼び出されます。
* @return
*/
public static boolean isTapped() {
return isTapped;
}

先ほどの C# のメソッドを Unity 側の Scripts フォルダに作った js で、定期的に呼び出すようにすればタップを確認できます。


function OnGUI() {
if (AndroidBehavior.isTapped()) {
// タップされたときの処理
}
}

上記コードでは、OnGUI で定期処理していますが、Update でも良いです。
どちらを選ぶかは、設計次第ですが、迷ったときは、Update が1回実行されると、OnGUI は2回実行されるということを判断材料にしてみてください。
タップのチェックで処理が遅くなったというのは体感できなかったので、問題ないと思います。

VOLUME_MUTE のイベント取得速度は決して早いものではなく、およそ1.5回/秒だったので、連打が必要なゲームには向かないかもしれません。

Artemis は、上下左右のフリックとタップで操作できるゲームです。
Nexus Q 版 Artemis では、次のようにゲーム操作を行なっています。

  • プレイヤーの左右の動き
  • ボリュームアップ/ボリュームダウン

  • 左右の打ち分け
  • 最後にボリュームを回した向き

  • 打ち返し
  • ボリュームミュートのタップ

  • 上下方向
  • 乱数による運

  • アプリ終了
  • 10秒間ボリュームミュートをロングタップ

3-1-min.jpg

3-2-min.jpg

3-3-min.jpg

アプリの終了は、一般的なAndroid端末では、Back キーを押下すれば良いですが、Nexus Q だと、そのままでアプリを終了するには、電源コードを抜く以外に方法はありません。
それだと、Nexus Q に優しくはないので、今回はロングタップで終了処理を実装しました。
しかし、ボールに手を置いたままで操作する人がいるかもしれないので、ロングタップに適度な時間を設けています。
今回、表示上は10秒にしましたが、実際に10秒は長く感じるので、7秒程度にしています。
その他、Nexus Q 版のために色々やったことは次のとおりです。

  • 広告
  • 画面をタップできないので非表示

  • ボタン
  • 選択状態がわかるように、選択時に文字色や背景色を変更

  • Twitter
  • アイコンがあるだけで選択できないように変更

  • キャラクター操作
  • フリックができないので、計算に扱う数値の調整

感想

Nexus Q に対応したアプリの開発は、難しいものではありませんが、キーボードからの入力しか受け付けない GUI を搭載した PC ソフトを作るようなもので、ボタンが複数あったときのボタンの選択状態の切り替えなどで、非常に面倒な実装をする必要があります。
課題は、アプリ側から、アプリを終了する処理を実装しても、アプリを起動するためには、adb 接続した PC からアプリを起動する必要があるため、Nexus Q 専用ランチャーアプリを開発する必要があることです。
また、アクション要素があるゲームだと、ボリュームをぐりぐり回すので、Nexus Q がすぐ壊れるような気がするので、ハードの耐久性を考えたゲーム内容にするべきだと感じました。

ブログ内の関連する記事