Java Advent Calendar 2012の11日目のエントリーです。
昨日は@cero_tさんです。
明日は@snuffkinさんです。

クリスマスだから楽しいことをしよう

ということで、WiiUも発売したことだし、今更ながら、WiiRemoteJを取り上げてみたいと思います。 WiiRemoteJはBluetoothを利用して、WiiリモコンでJavaのアプリを操作するためのライブラリです。 最新版はv1.6というのがあるようなのですが、見つけることができなかったため、v1.4を使ってみたいと思います。

OSX 10.8.7 Mountain Lionで動かす

今回やりたいことは、Macbook Pro上にWiiリモコンのレシーバーとなるアプリを起動し、 Wiiリモコンを使ってそのアプリを操作する、ということです。 レシーバーアプリは下記2つのライブラリが必要になります。

BlueCoveはJavaのBluetoothを利用する為のAPIの規約であるJSR-82の実装ライブラリです。 WiiRemoteJを動かす為に必要になります。 BlueCoveの最新版は2.1.0です。ところがこのBlueCove-2.1.0、Mountain Lion上では動かす事ができません。 Mountain Lionで動かす為には、まだ正式にリリースされていないBlueCove-2.1.1-SNAPSHOTを利用する必要があります。

ただ、この2.1.1-SNAPSHOTも問題があります。 BlueCoveはBluetoothにアクセスする為に/System/Library/Frameworks/IOBluetooth.frameworkというフレームワークを利用しているのですが、 BlueCove-2.1.1-SNAPSHOTはこのIOBluetooth.frameworkに対応できていないため、実行時にエラーが発生してしまいます。

という訳で、このままでは実行できそうにありません。 そこで、ここで提供されている、 BlueCove-2.1.1-SNAPSHOTで操作できるIOBluetooth.frameworkに置き換えることにします。 置き換える事により、他のアプリで問題が発生するかもしれません。 置き換える前に、元のIOBluetooth.frameworkのバックアップを作る事をお勧めします。

さあ、ここまでできれば、あとはレシーバーを作るだけです。

レシーバーを実装する

下記が実装コードになります。動作は単純で、1ボタン、2ボタン、マイナスボタン、プラスボタン、Aボタン、Bボタン、十字キーを押した場合は、 それを標準出力に表示、ホームボタンを押したらレシーバーを終了します。

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import wiiremotej.WiiRemote;
import wiiremotej.WiiRemoteJ;
import wiiremotej.event.WRAccelerationEvent;
import wiiremotej.event.WRButtonEvent;
import wiiremotej.event.WRStatusEvent;
import wiiremotej.event.WiiRemoteAdapter;
import wiiremotej.event.WiiRemoteDiscoveredEvent;
import wiiremotej.event.WiiRemoteDiscoveryListener;

/**
 * WiiRemoteJサンプルアプリ
 * @author Katsumi
 */
public class Wii extends WiiRemoteAdapter implements WiiRemoteDiscoveryListener
{
    private WiiRemote _remote;

    public static void main(String... args)
    {
        Wii wii = new Wii();
        WiiRemoteJ.findRemotes(wii, 1);
    }

    @Override
    public void wiiRemoteDiscovered(WiiRemoteDiscoveredEvent evt)
    {
        _remote = evt.getWiiRemote();
        try {
            _remote.setAccelerometerEnabled(true);
            _remote.setSpeakerEnabled(true);
            _remote.setLEDIlluminated(0, true);
        }
        catch (IOException | IllegalStateException ex) {
            Logger.getLogger(Wii.class.getName()).log(Level.SEVERE, null, ex);
            if (null != _remote && _remote.isConnected()) {
                _remote.disconnect();
            }
        }

        _remote.addWiiRemoteListener(this);
    }

    @Override
    public void disconnected()
    {
    }

    @Override
    public void findFinished(int numFound)
    {
    }

    @Override
    public void statusReported(WRStatusEvent evt)
    {
    }

    @Override
    public void accelerationInputReceived(WRAccelerationEvent evt)
    {
    }

    @Override
    public void buttonInputReceived(WRButtonEvent evt)
    {
        if (evt.wasPressed(WRButtonEvent.TWO)) {
            System.out.println("2");
        }
        else if (evt.wasPressed(WRButtonEvent.ONE)) {
            System.out.println("1");
        }
        else if (evt.wasPressed(WRButtonEvent.B)) {
            System.out.println("B");
        }
        else if (evt.wasPressed(WRButtonEvent.A)) {
            System.out.println("A");
        }
        else if (evt.wasPressed(WRButtonEvent.MINUS)) {
            System.out.println("Minus");
        }
        else if (evt.wasPressed(WRButtonEvent.PLUS)) {
            System.out.println("Plus");
        }
        else if (evt.wasPressed(WRButtonEvent.LEFT)) {
            System.out.println("Left");
        }
        else if (evt.wasPressed(WRButtonEvent.RIGHT)) {
            System.out.println("Right");
        }
        else if (evt.wasPressed(WRButtonEvent.DOWN)) {
            System.out.println("Down");
        }
        else if (evt.wasPressed(WRButtonEvent.UP)) {
            System.out.println("Up");
        }
        else if (evt.wasPressed(WRButtonEvent.HOME)) {
            if (null != _remote && _remote.isConnected()) {
                _remote.disconnect();
            }
            System.exit(0);
        }
    }
}

動かしてみる

レシーバー起動時のVMオプションに下記を設定します。 このオプションを設定しないと、実行時にエラーが発生します。

-Dbluecove.jsr82.psm_minimum_off=true

レシーバー起動後にWiiリモコンの電池脇にある赤いSyncボタンを押す必要があります。数秒待つとアプリとWiiリモコンが繋がります。

実際に動かしているときの動画です。

まとめ

本当はボタンを押したらWiiリモコンで音を鳴らすということもしてみたかったのですが、 エラーが出て音源ファイルがWiiリモコンにうまく転送されませんでした。 仕組みとしては、アプリ側からのアクションでWiiリモコン側で音を鳴らす事もできるはずです。 音が出せるようになれば、ちょっとしたパーティーゲームができそうなので、この時期にはもってこいの遊びではないでしょうか。