Zeemoteを使ったAndroidのプログラムを組んでみました
Google Developer Day 2011にてZeemoteというデバイスを配っていました。ZeemoteというのはAndroid用のbluetoothのコントローラで、動かした様子は下のような感じ。
GAPSIS.JP様の映像を引用…
GDD2011から1ヶ月くらい放置していたのですが、ちょっと時間ができたので、少しいじって見ました。
とりあえず触ってみる
なにはともあれ、ZeemoteとAndroidをペアリングしなければ動かすことはできません。ペアリングは、[設定]-[無線とネットワーク]-[Bluetooth]にチェックを入れて、さらにその下の[Bluetoothの設定]から出来ます。
Zeemoteの電源を入れて、付近のデバイスを検索します。
見つかると、Zeemote JS1 Hというのがリストに表示されます。
Zeemote JS1 Hを押して、PIN「0000」を入力します。
そうすると、ペアリングされたデバイスにZeemote JS1 Hが表示されます。
Zeemoteのページを見ると、R-TYPEというゲームでZeemoteが使えるみたいなので、早速ダウンロードしてプレイすると…、おお、確かにZeemoteで動く。
SDKのダウンロード
http://www.zeemote.com/にアクセスすると、右上に「DOWNLOAD ZDK」というリンクがあります。押してみましょう。
すると、開発者ページが現れます。SDKをダウンロードするためには開発者登録が必要なので、必要事項を記入して、登録をしてください。
登録ができたら、開発者ページからログインをすることで、SDKダウンロードページに行くことができます。
Androidで開発をしようと思うので、Zeemote Android SDK 1.6.0をダウンロードします。
ダウンロードしたzipファイルを展開するとこんな感じ。
早速、Eclipseを起動して、examplesの下のandroid-sampleをインポートしてみます。
そして、実行。Zeemoteのボタンやスティックを動かしていると、なんか表示されています。
表示はこんな感じ。スティックの傾きや、ボタンのPress、Releaseが表示されています。
オリジナルのプログラムを作成する
サンプルのソースを見てみると、案外難しくなさそうに見えます。buttonPressed関数はボタンが押されたときに呼ばれ、buttonReleased関数はボタンが離されたときに呼ばれます。2つの関数はButtonEventを引数に取りますが、ButtonEventにボタンの情報が格納されているのでしょう。joystickMoved関数も名前そのままで、スティックを動かしたときに呼ばれます。他にもconnected/disconnected関数があり、これらの関数は、IStatusListener, IJoystickListener, IButtonListenerを実装(Implements)する必要があるため、ソースコードに記述されています。
1からプログラムを作るときは、SDKに含まれているAndroid Programmer's Guideの5ページ "How To Use"に沿えば大丈夫だと思います。
まず、SDKのlibsフォルダをプロジェクトにコピーして、ビルドパスを通します。
また、bluetoothを使用するため、 android.permission.BLUETOOTH と android.permission.BLUETOOTH_ADMIN の許可をAndroidManifest.xmlに書き込みます。
そして、Zeemoteとの接続に必要なUIはライブラリが提供するものを使用したいため、com.zeemote.zc.ui.android.ControllerAndroidUi$Activity というアクティビティをアプリケーションから呼び出す設定をAndroidManifest.xmlに書き込みます。
ソースコードはサンプルからコピペしてきて、必要な部分を編集するといいと思います。私はmp3ファイルを4つロードして、ボタンやスティックが操作されたら、アサインしたmp3を再生するというプログラムを作ってみました。
mp3の再生の部分を手を抜いているため、サウンドに重ねてサウンドを再生することができていませんが、一応音が鳴っているので、Zeemoteでの操作を受け取っていることが分かります。あとは、どんどん改造していけばいいですね。
下記に実際に作ったソースを掲載します。あまり良いコードではないのですが、参考になれば幸いです。
package hoge.zeemote.test2; import java.io.IOException; import com.zeemote.zc.Controller; import com.zeemote.zc.event.BatteryEvent; import com.zeemote.zc.event.ButtonEvent; import com.zeemote.zc.event.ControllerEvent; import com.zeemote.zc.event.DisconnectEvent; import com.zeemote.zc.event.IButtonListener; import com.zeemote.zc.event.IJoystickListener; import com.zeemote.zc.event.IStatusListener; import com.zeemote.zc.event.JoystickEvent; import com.zeemote.zc.ui.android.ControllerAndroidUi; import android.app.Activity; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; public class Zeemote_test2Activity extends Activity implements IStatusListener, IJoystickListener, IButtonListener { private static final int MENU_CONTROLLER = 1; private Controller controller; private ControllerAndroidUi controllerUi; private boolean controllerUiShown; public static final int GUIUPDATEIDENTIFIER = 0x101; private String info; MediaPlayer _hh; MediaPlayer _bd; MediaPlayer _sd; MediaPlayer _mac; TextView label; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); controller = new Controller(1); controller.addStatusListener(this); controller.addButtonListener(this); controller.addJoystickListener(this); controllerUi = new ControllerAndroidUi(this, controller); controllerUiShown = false; _hh = MediaPlayer.create(this, R.raw.hh); _bd = MediaPlayer.create(this, R.raw.bd); _sd = MediaPlayer.create(this, R.raw.sd); _mac = MediaPlayer.create(this, R.raw.mac); label = (TextView)findViewById(R.id.lblText); label.setTextSize( (float) 120.0); } @Override public void onResume() { super.onResume(); if (!controllerUiShown) { // show the controller activity. controllerUi.startConnectionProcess(); controllerUiShown = true; } else { // the controller activity was hidden. controllerUiShown = false; } try { _hh.prepare(); _bd.prepare(); _sd.prepare(); _mac.prepare(); } catch (IllegalStateException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } catch (IOException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } } @Override public void onPause() { super.onPause(); if (!controllerUiShown) { // If the controller activity is not shown, // disconnect the Zeemote controller. try { controller.disconnect(); } catch (Exception e) { } } } /** * Invoked during init to give the Activity a chance to set up its Menu. * * @param menu * the Menu to which entries may be added * @return true */ @Override public boolean onCreateOptionsMenu(final Menu menu) { super.onCreateOptionsMenu(menu); menu.add(0, MENU_CONTROLLER, 0, "Controller"); return true; } /** * */ @Override public boolean onPrepareOptionsMenu(final Menu menu) { super.onPrepareOptionsMenu(menu); return true; } /** * Invoked when the user selects an item from the Menu. * * @param item * the Menu entry which was selected * @return true if the Menu item was legit (and we consumed it), false * otherwise */ @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case MENU_CONTROLLER: controllerUi.showControllerMenu(); controllerUiShown = true; return true; } return false; } public void buttonPressed(ButtonEvent arg0) { // TODO 自動生成されたメソッド・スタブ // button id 0:A(bd), 1:B(sd), 2:C, 3:power int b = arg0.getButtonID(); if(b==0){ _bd.start(); traceLog("BD"); } else if(b==1){ _sd.start(); traceLog("SD"); } } public void buttonReleased(ButtonEvent arg0) { // TODO 自動生成されたメソッド・スタブ } public void joystickMoved(JoystickEvent arg0) { // TODO 自動生成されたメソッド・スタブ int x = arg0.getScaledX(-100, 100); int y = arg0.getScaledY(-100, 100); if(x < -50){ _hh.start(); traceLog("HH"); } if(x > 50){ _mac.start(); traceLog("MAC"); } } public void batteryUpdate(BatteryEvent arg0) { // TODO 自動生成されたメソッド・スタブ } public void connected(ControllerEvent arg0) { // TODO 自動生成されたメソッド・スタブ } public void disconnected(DisconnectEvent arg0) { // TODO 自動生成されたメソッド・スタブ } protected void traceLog(final String s) { info = s; Message m = new Message(); m.what = GUIUPDATEIDENTIFIER; this.ViewUpdateHandler.sendMessage(m); } Handler ViewUpdateHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case GUIUPDATEIDENTIFIER: label.setText(info); break; } super.handleMessage(msg); } }; }