swampの忘備録

エンジニアが、情報系のイベント行ったときとかプログラミングなどの情報工学について忘備録として書くつもりです。

UnityWiimoteを利用して複数台のWiiリモコンを制御する。

 去年の8月~11月にかけて、学園祭の出し物でWiiリモコンをUnityで複数台制御してゲームを作りました。

 今回はそのゲームのコア技術であったWiiリモコンの複数台制御の方法について解説します。

 結論から言ってしまうとUnity-Wiimoteとものを使えば簡単にできます。

 日本語能力がないので長いわりによく分かりませんがお付き合いください。

 

1.なぜ今頃になってこの記事を書こうと思ったのか。

 このツイートが今になって同じことで悩んでいらっしゃる方にリツイートされたので、少しブログを書くモチベーションができました。(ずっと書こう書こうと後回しにし続けてきた)

 僕にモチベーションを与えてくださり、ありがとうございます。

 

2.準備

Wiiリモコン(複数台)

Windows7(推奨)のPC

 僕の環境ならば、何度やってもWindows7以降ではうまくいきません。

 アダプタの相性が悪かったのかもしれません。

 Windows8、10でももしかしたらできるかもしれません。

 ※ 以下、都合上Windows10の画面が出てきますが動作確認はしてません。

Wiiリモコンのセンサーバー

 Wiiの上についてるあれです。あれがないとWiiリモコンを動かすことはできません。

以下のページなどを参考に自分で作ることもできます。「Wiiリモコン センサーバー 自作」で検索したら結構でてきます。

tarukosu.hatenablog.com

Bluetoothのアダプタまたは内蔵PC

 BluetoothがないとWiiリモコンの制御はできません。

 

・Unity-Wiimote

 海外の方が作ってくださったソフトウェアです。

 Unity-Wiimoteのおかげで学園祭の出し物は成功したので感謝しても感謝しきれません。

 この場を借りてお礼申し上げます。

 以下のページからダウンロードしてください。

github.com

 UnityPackageのみのものでもよいですが、動作確認用のデモもあるほうがよいと思います。したがって「Sorce code(zip)」と書かれているほうを勧めます。以降はUnity-Wiimoteのデモのプログラムを参考にして複数台制御の方法を説明します。


3.Wiiリモコンの接続

 WiiリモコンをPCに接続してください。

 Wiiリモコンの電池カバーをはがしたところにあるボタンを押して接続すればできると思います。

 詳細な方法は省略しますが、質問があればコメントしてください。

 

4.Unity-Wiimoteの起動

 さて、Unity-Wiimoteを起動しましょう。

 まず、ダウンロードした「Unity-Wiimote-1.1.zip」を展開します。

 そして、展開したファイルを開き、Assetフォルダの中にあるSceneという名前のUnity scene fileを開く。

f:id:swamptk:20180120204424p:plain

 すると、以下のような画面が表示される。

f:id:swamptk:20180120215601p:plain

 開いたときにこのような状態になる場合は、Blenderをインストールしましょう。

f:id:swamptk:20180120211804p:plain


5 Scriptを追加する

 今開いているSceneはAsset>Scriptの中にあるWiimoteDemo.csをScriptとして動いています。

 このSceneを実行すると1つのWiiリモコンを制御することができます。

 このプログラムを参考に複数台制御に拡張していきます。

 この記事では、以下の動画の4番「IR Camera Saport」を複数台で行う方法を説明します。

 
www.youtube.com


 今回は、2台制御として話を進めていきます。

 Asset>Scriptの中に新たにWiimoteTest.csを追加します。
 
 Asset>Scriptに移動し、左クリック→Create→C# Scriptをクリックし、Scriptを作成する。
 
 名前は、WiimoteTestにする。
 
 f:id:swamptk:20180121004948p:plain

 以下に示すWiimoteTest.csというプログラムは、実行すると2台のWiiリモコンでポインタの操作が可能になります。

 ※注意 今回は簡単のため、ボタン等の制御の説明はしません。ExtDataDemo.csなどを参考に同じ理論で考えてみてください。

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Text;
using WiimoteApi;

public class WiimoteTest : MonoBehaviour {

    public RectTransform[] ir_dots0;
    public RectTransform[] ir_dots1;
    public RectTransform[] ir_bb0;
    public RectTransform[] ir_bb1;
    public RectTransform ir_pointer0;
    public RectTransform ir_pointer1;

    private Wiimote wiimote0;
    private Wiimote wiimote1;

    private int flag = 0;

    void Start() {
	WiimoteManager.FindWiimotes();
    }

	void Update () {
        if (!WiimoteManager.HasWiimote()) { return; }

        wiimote0 = WiimoteManager.Wiimotes[0];
	wiimote1 = WiimoteManager.Wiimotes[1];

        int ret0;
	int ret1;
        do
        {
            ret0 = wiimote0.ReadWiimoteData();
            ret1 = wiimote1.ReadWiimoteData();

        } while (ret0 > 0 || ret1 > 0);

        if (ir_dots0.Length < 4 || ir_dots1.Length <4) return;

        float[,] ir0 = wiimote0.Ir.GetProbableSensorBarIR();
	float[,] ir1 = wiimote1.Ir.GetProbableSensorBarIR();
	
        for (int i = 0; i < 2; i++)
        {
            float x0 = (float)ir0[i, 0] / 1023f;
            float y0 = (float)ir0[i, 1] / 767f;
	    float x1 = (float)ir1[i, 0] / 1023f;
            float y1 = (float)ir1[i, 1] / 767f;

            if (x0 == -1 || y0 == -1) {
                ir_dots0[i].anchorMin = new Vector2(0, 0);
                ir_dots0[i].anchorMax = new Vector2(0, 0);
            }

	    
            if (x1 == -1 || y1 == -1) {
                ir_dots1[i].anchorMin = new Vector2(0, 0);
                ir_dots1[i].anchorMax = new Vector2(0, 0);
            }

	    

            ir_dots0[i].anchorMin = new Vector2(x0, y0);
            ir_dots0[i].anchorMax = new Vector2(x0, y0);

	    ir_dots1[i].anchorMin = new Vector2(x1, y1);
            ir_dots1[i].anchorMax = new Vector2(x1, y1);

            if (ir0[i, 2] != -1)
            {
                int index = (int)ir0[i,2];
                float xmin0 = (float)wiimote0.Ir.ir[index,3] / 127f;
                float ymin0 = (float)wiimote0.Ir.ir[index,4] / 127f;
                float xmax0 = (float)wiimote0.Ir.ir[index,5] / 127f;
                float ymax0 = (float)wiimote0.Ir.ir[index,6] / 127f;
                ir_bb0[i].anchorMin = new Vector2(xmin0, ymin0);
                ir_bb0[i].anchorMax = new Vector2(xmax0, ymax0);
            }

	    if (ir1[i, 2] != -1)
            {
                int index = (int)ir1[i,2];
                float xmin1 = (float)wiimote1.Ir.ir[index,3] / 127f;
                float ymin1 = (float)wiimote1.Ir.ir[index,4] / 127f;
                float xmax1 = (float)wiimote1.Ir.ir[index,5] / 127f;
                float ymax1 = (float)wiimote1.Ir.ir[index,6] / 127f;
                ir_bb1[i].anchorMin = new Vector2(xmin1, ymin1);
                ir_bb1[i].anchorMax = new Vector2(xmax1, ymax1);
            }


        }

        float[] pointer0 = wiimote0.Ir.GetPointingPosition();
	float[] pointer1 = wiimote1.Ir.GetPointingPosition();

        ir_pointer0.anchorMin = new Vector2(pointer0[0], pointer0[1]);
        ir_pointer0.anchorMax = new Vector2(pointer0[0], pointer0[1]);

	ir_pointer1.anchorMin = new Vector2(pointer1[0], pointer1[1]);
		ir_pointer1.anchorMax = new Vector2 (pointer1 [0], pointer1 [1]);


	if(flag ==0){
		wiimote0.SetupIRCamera(IRDataType.BASIC);
		wiimote1.SetupIRCamera(IRDataType.BASIC);
		flag++;
	}


	}

    private void OnApplicationQuit(){
	WiimoteManager.Cleanup(wiimote0);
	WiimoteManager.Cleanup(wiimote1);
	wiimote0 = null;
	wiimote1 = null;
    }

}

 思ったより書くのが大変だったので詳細なプログラムの解説はしません。頑張って解読してください。

 簡単に言えば、WiimoteDemo.csからポインタ制御の部分だけ抜き取ってきて、

 Wiimote型の変数wiimote0とwiimote1を作成し、それぞれに変数名だけ変更してWiimoteDemo.csと同じプログラムを書いてあげればいい。

 要は、Wiiリモコンやポインタ関係の処理をひたすら複製していくという作業をします。

 下部のLEDやボタンの操作もこのようなやり方で簡単にできます。
 
 質問がありましたら、コメント等でお願いします。

 気が向いたら追記します。


6 IR Dot Canvasの追加
 
 このプログラムでは、複数のIR Dot Canvasを用意してそれぞれのWiiリモコンに対応させます。
 
 1つのWiiリモコンのポインタを表示させるためには、1つのIR Dot Canvasが必要です。

 IR Dot Canvasをコピー&ペーストして以下の状態にします。

 f:id:swamptk:20180121014509p:plain

 あとで、分からなくならないように「IR Dot Canvas (1)」とその中の「Panel」や「BB」の名前を以下のように変えておきます。
f:id:swamptk:20180121170224p:plain

  
7 スクリプトの追加

 InspecterタブのAdd Component→Scriptを押し、WiimoteTest.csをスクリプトに追加します。

 WiimoteDemo.csに入っている☑は外しましょう。

f:id:swamptk:20180121165611p:plain

f:id:swamptk:20180121165702p:plain

 詳細を設定していきます。

 複数台使用するので、Ir_dots、Ir_pointer、Ir_bbをそれぞれ複数設定する必要があります。

 このプログラムでは、Ir_dots0、Ir_pointer0、Ir_bb0とIr_dots1、Ir_pointer1、Ir_bb1を用意しています。

 3台、4台で制御するのであれば、これらをもっと増やしていけばできます。

 この変数に先ほど追加したIR Dot Canvas2の要素を追加していきます。

 0と書かれた変数には、もとからあったIR Dot Canvasの要素を、

 1と書かれた変数には、追加したIR Dot Canvas2の要素を追加します。

 f:id:swamptk:20180121172236p:plain


8 実行

 実行ボタンを押せば、2台でのポインタ制御ができているはずです。

 僕は、ソースコード理解能力がないのでたったこれだけのことをやるのに2か月もかかってしまいました。

(他にもいろいろな方法を試したがどれもうまくいかなかった)


9 最後に

 Wiiリモコンなんてオワコンという人もいるとは思いますがまだまだ可能性は無限大です。

 いろいろなことができます。 

 ボタンも使えるし、ジャイロセンサーにもなるし。
 
 弊学校の学園祭の出し物ではWiiリモコンを4台利用したシューティングゲームを作成しました。

 こんな感じ。(画像があらくて分かりにくいけど)

f:id:swamptk:20180121191335p:plain

f:id:swamptk:20180121191406p:plain

f:id:swamptk:20180121191414p:plain

 
(これに関しては今度記事を書くかもしれないです。)

 皆さんも是非、Wiiリモコンの複数台制御を試してUnityでゲームを作ってみてください!



参考ページ
kamera25の座敷部屋 Unity から Wiiリモコン を使ってみる ~その1~

Wii のセンサーバーを自作する - たるこすの日記

Wiimote API for C# and Unity - YouTube