赤外線センサ(IRセンサ)を使ってみる
Wiiコントローラで最も特筆すべき優秀な部位と言えば, IRセンサでしょう.
任天堂とPixArtの共同で開発されたこのCMOSセンサは毎秒200フレームで動作し, 最大4点の光の強さと二次元座標を同時に取得できます.
Wii付属のセンサバーは両側に赤外線LEDの発光源があり, この二点から三角測量によってテレビとの距離を算出したり, コントローラのねじれを算出することができます.
先ほど提示したmywiimote.cppとmywiimote.hではWiistate.Irの配列から, 光の強さをSizeで二次元座標をXとYで生データの二次元座標をRawXとRawYで取得できます.
初めは0〜1までの値で調べられる[1]XやYで十分ですが, 二点で測量したりキャリブレーションを行いたいという場合には物足りません.
そこで, キャリブレーションを行う方法を説明します.
比較的簡単に行えるので, 分かる人は飛ばしても結構です.
今回は簡単のため, 光点が一つであるとします.
センサバーを自作した場合はまとまったLED郡を一つだけディスプレイの上に置き, Wii付属のセンサバーの場合は端を片方だけアルミホイルなどで隠してディスプレイの上に置いて実験してください.
RawXは0〜1023までの値, RawYは0〜767までの値がでます.
このときに, RawXはコントローラが右に行くと小さい値に, 左に行くと大きい値になり, RawYはコントローラが上に行くと小さい値に, 下に行くと大きい値になります.
ここで, ディスプレイの左上をWiiコントローラが指しているときに次のような代入を行います.
IRXmax = Wiistate.Ir[maxsize].RawX (1)
IRYmin = Wiistate.Ir[maxsize].RawY (2)
同様に, ディスプレイの右下をWiiコントローラが指しているときに次のような代入を行います.
IRXmin = Wiistate.Ir[maxsize].RawX (3)
IRYmax = Wiistate.Ir[maxsize].RawY (4)
ただし, maxsizeとは最も光の強さが大きかった点を示し, 次のコードで調べられます.
for(int t=0; t<4; t++){
if(Wiistate.Ir[t].bVisible){
if(tempsize > Wiistate.Ir[t].Size){
tempsize = Wiistate.Ir[t].Size;
maxsize = t;
}
}
}
(1)〜(4)式と次の式を合わせて見てください.
これらの式によって求められたIRPOINTXとIRPOINTYという値は, 表示した画面が640×480のサイズだった場合のコントローラが指し示す座標です.
後はこのIRXmin, IRXmax, IRYmin, IRYmaxという変数の値を保存しておけば, キャリブレーションは終了です.
画面サイズが異なるのであれば, (5)式の640という値をX座標のサイズに, (6)式の480という値をY座標のサイズに置き換えて計算してください.
ヌンチャクを使ってみる
WiiYourself!の最新版はヌンチャクやモーションプラスにも対応しています.
そこで, ヌンチャクに対応するためにmywiimote.cppに以下のコードを追加します.
これでヌンチャクに付いているボタンの状態を取得させます.
void wiinunbutton(WIISTATE &Wiistate)
{
if(cWiiRemote->Nunchuk.C) Wiistate.NunchukButton[WiiNunchukKeyNum::C]++;
else Wiistate.NunchukButton[WiiNunchukKeyNum::C] =
-(Wiistate.NunchukButton[WiiNunchukKeyNum::C]>0);
if(cWiiRemote->Nunchuk.Z) Wiistate.NunchukButton[WiiNunchukKeyNum::Z]++;
else Wiistate.NunchukButton[WiiNunchukKeyNum::Z] =
-(Wiistate.NunchukButton[WiiNunchukKeyNum::Z]>0);
}
そして, mywiimote.cppのwiiinit関数を次のコードに書き換えます.
これでヌンチャクが有る場合はそれを読み込むための初期化を行います.
int wiiinit()
{
int t=0;
cWiiRemote = new wiimote();
// 接続開始
for(t=0; t<4; t++){
if(cWiiRemote->Connect(wiimote::FIRST_AVAILABLE)) break;
Sleep(1000);
}
// ボタン, 加速度, 赤外線センサー, その他取得用
if(cWiiRemote->NunchukConnected()){
cWiiRemote->SetReportType(wiimote::IN_BUTTONS_ACCEL_IR_EXT);
}else{
cWiiRemote->SetReportType(wiimote::IN_BUTTONS_ACCEL_IR);
}
if(t==4) return 0;
return 1;
}
そして, mywiimote.cppのgetwiistate関数の最後に次のコードを追加します.
これでヌンチャクの加速度センサーとジョイスティックの状態を取得します.
// ヌンチャク処理
Wiistate.NunchukCon = cWiiRemote->NunchukConnected();
if(Wiistate.NunchukCon){
// ボタンの状態取得
wiinunbutton(Wiistate);
// ヌンチャクの加速度センサーのデータ取得
Wiistate.NunchukAccel.X = cWiiRemote->Nunchuk.Acceleration.X; // x座標
Wiistate.NunchukAccel.Y = cWiiRemote->Nunchuk.Acceleration.Y; // y座標
Wiistate.NunchukAccel.Z = cWiiRemote->Nunchuk.Acceleration.Z; // z座標
Wiistate.NunchukAccel.ABS = sqrt(Wiistate.NunchukAccel.X*Wiistate.NunchukAccel.X
+ Wiistate.NunchukAccel.Y*Wiistate.NunchukAccel.Y
+ Wiistate.NunchukAccel.Z*Wiistate.NunchukAccel.Z);
// ヌンチャクのジョイスティックの状態取得
Wiistate.NunchukJoy.X = cWiiRemote->Nunchuk.Joystick.X;
Wiistate.NunchukJoy.Y = cWiiRemote->Nunchuk.Joystick.Y;
}
追加したコードに合わせてヘッダファイルも調整します.
まずは, mywiimote.hの列挙体の後ろに次の列挙体を追加します.
enum WiiNunchukKeyNum{
C,
Z,
NUNMAX
};
そして, mywiimote.hのWIISTATE構造体の最後に次のコードを追加します.
これで, ヌンチャクを使う準備が出来ました.
bool NunchukCon;
int NunchukButton[WiiNunchukKeyNum::NUNMAX];
struct nunchukaccel{
float X;
float Y;
float Z;
float ABS;
}NunchukAccel;
struct nunchukjoystick{
float X;
float Y;
}NunchukJoy;
使い方はソースコードを読めば大体分かるでしょう.
ちなみにジョイスティックのX座標とY座標は-1〜1の値を取ります.
これらを全て付け加えたコードは, wii_controler.zipのmywiimote_nun.cppとmywiimote_nun.hです.
補足
- Xは0が左で1が右, Yは0が上で1が下をあらわしています.