KOSAKA LABORATORY->Tips

 このBlogの一部が書籍になりました。
書籍:「WiiRemoteプログラミング」
著者:白井暁彦・小坂崇之・木村秀敬・くるくる研究室
表紙:タナカユカリ
発売:2009年7月25日 好評発売中
詳細:こちらから

内容:WiiRemoteを利用したインタラクティブ技術のプログラミング学習書

 話題の家庭用ゲーム機「Wii」の特徴的なコントローラーである「WiiRemote」をPCで使うプログラミングが注目されている。高性能な加速度センサーや赤外線センサーを持ち、Bluetooth接続が可能で、プログラミング可能なデバイスを、個人レベルで自作したり入手したりすることはできなかった。WiiRemoteの登場とともにそれが安価で手軽に入手可能になり、多くの人々が興味を持っている。

 本書は、WiiRemoteの応用に興味があるプログラミング初学者向けに、WiiRemoteをPCから扱うためのプログラミングを解説する。ハードウェア寄りのゲームプログラミング入門書として、ステップバイステップでソースコードを解説する。具体的なサンプルを開発しながら、インタラクション技術を独習できる。言語はC/C++、C#およびActionScript 3、Processingなどをサポート。

wiiリモコン 赤外線カメラ その5 「Wiiリモコンでマウス作成2」

 前回に引き続きWiiリモコンの赤外線カメラを用いたマウスの応用プログラミングを行っていきます。
今回は、マウスの座標が反転していた点を改良し、左クリックと右クリックを実装します。

1.Wii赤外線マウスプログラム2
Form1.csに以下の部分を追加する。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using WiimoteLib;                       //WimoteLibの宣言
using System.Runtime.InteropServices ;  //DllImportを使うための宣言


namespace WiimoteLib_Sample {


   public partial class Form1 : Form {


      //DLL読み込み用
      [DllImport("user32.dll")]
      extern static uint SendInput( uint nInputs, INPUT[] pInputs, int cbSize   );

      //DLL読み込み用
      [StructLayout(LayoutKind.Sequential)]
      struct INPUT {
         public int type;
         public MOUSEINPUT mi;
      }

      //DLL読み込み用
      [StructLayout(LayoutKind.Sequential)]
      struct MOUSEINPUT {
         public int dx;
         public int dy;
         public int mouseData;
         public int dwFlags;
         public int time;
         public IntPtr dwExtraInfo;
      }
 
      Boolean isADown = false;                                     //Aボタンが押されたか判定フラグ
      Boolean isBDown = false;                                     //Bボタンが押されたか判定フラグ
 
      Wiimote wm = new Wiimote();                                  //Wiimoteの宣言
      
      System.Drawing.Point ScreenSize;                             //画面サイズを格納
      
      public Form1() {
                  
         InitializeComponent();
         
         this.ScreenSize.X = Screen.PrimaryScreen.Bounds.Width;    //画面ザイズの横幅を取得
         this.ScreenSize.Y = Screen.PrimaryScreen.Bounds.Height;   //画面ザイズの立幅を取得
                  
         //他スレッドからのコントロール呼び出し許可
         Control.CheckForIllegalCrossThreadCalls = false;
      }

      //接続ボタンが押されたら
      private void button1_Click(object sender, EventArgs e) {
         this.wm.Connect();                                        //Wiimoteの接続
         this.wm.WiimoteChanged += wm_WiimoteChanged;              //イベント関数の登録
         this.wm.SetReportType(InputReport.IRExtensionAccel, true);//レポートタイプの設定

      }
      //切断ボタンが押されたら
      private void button2_Click(object sender, EventArgs e) {
         this.wm.Disconnect();                                     //Wiimote切断
      }

      //Wiiリモコンの値が変化する度に呼ばれる
      void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args){
         WiimoteState ws = args.WiimoteState;                      //WiimoteStateの値を取得
         this.DrawForms(ws);                                       //フォーム描写関数へ
      }


      //フォーム描写関数
      public void DrawForms(WiimoteState ws) {
         INPUT[] input = new INPUT[1];                             //マウスイベントを格納
         Graphics g = this.pictureBox1.CreateGraphics();           //グラフィックスを取得

         g.Clear(Color.Black);                                     //画面を黒色にクリア

         //もし赤外線を1つ発見したら
         if (ws.IRState.IRSensors[0].Found) {
            //赤色でマーカを描写
            g.FillEllipse( Brushes.Red,
                           ws.IRState.IRSensors[0].Position.X * 200 ,
                           ws.IRState.IRSensors[0].Position.Y * 200 , 10 , 10 );


            //赤外線座標(0.0~1.0)を画面サイズと掛け合わせる
            int px = (int)(ws.IRState.IRSensors[0].Position.X * this.ScreenSize.X);
            int py = (int)(ws.IRState.IRSensors[0].Position.Y * this.ScreenSize.Y);

            //X座標を反転させる
            px = this.ScreenSize.X - px;

            //マウスカーソルを指定位置へ移動
            System.Windows.Forms.Cursor.Position = new System.Drawing.Point(px, py);
      
         }

         //Aボタン処理
         if (ws.ButtonState.A == true) {
            //もしAボタンが押されたら
            if (this.isADown == false) {
               Console.WriteLine("Aおされた");
               input[0].mi.dwFlags = 0x0002;                       //左マウスダウン
               SendInput(1, input, Marshal.SizeOf(input[0]));      //マウスイベントを送信
               this.isADown = true;
            }
         } else {
            if (this.isADown == true) {
               //もしAボタンが押されていて離されたら
               Console.WriteLine("Aはなされた");
               this.isADown = false;

               input[0].mi.dwFlags = 0x0004;                      //左マウスアップ
               SendInput(1, input, Marshal.SizeOf(input[0]));     //マウスイベントを送信
            }
         }

         //Bボタン処理
         if (ws.ButtonState.B == true) {
            //もしBボタンが押されたら

            if (this.isBDown == false) {
               Console.WriteLine("Bおされた");

               input[0].mi.dwFlags = 0x0008;                     //右マウスダウン
               SendInput(1, input, Marshal.SizeOf(input[0]));    //マウスイベントを送信

               this.isBDown = true;
            }
         } else {
            if (this.isBDown == true) {
               //もしBボタンが押されていて離されたら
               Console.WriteLine("Bはなされた");
               this.isBDown = false;
               input[0].mi.dwFlags = 0x0010;                    //右マウスアップ
               SendInput(1, input, Marshal.SizeOf(input[0]));   //マウスイベントを送信
            }
         }

         g.Dispose();                                           //グラフィックスを開放

      }
   }
}
実行

1.WiiRemoteを接続してください。

2.F5キーを押して実行してください。


3.接続ボタンをクリックしてください。
  ※もしエラーが発生する場合はWiiRemoteが正しく接続されているか確認してください。


4.WiiリモコンをWiiセンサーバーに向けてください。

 赤外線が1つ検出されると図のように赤い点が表示され、マウスカーソルが移動します。赤外線を検出している間にマウスカーソルを動かそうとしても思い通りに動きません。

 マウスカーソルの動きが激しすぎる場合は、Wiiとセンサーバーとの距離を2m~3mまで離してください。距離が長いほど安定した動きを行うことができます。

※センサーバーがない場合は、赤外線が発生するもの(太陽、ロウソクの火、テレビのリモコン、携帯電話の赤外線通信 など)に向けてください。

 


5.WiiリモコンのAボタンBボタンをクリック

 WiiリモコンのAボタンで左クリック、Bボタンで右クリックを行います。環境によっては若干マウス動作が遅くなるかと思います。ドラック動作を行う場合は、ゆっくりAボタンを押し続けて行うと良いです。

 

終了する場合は

 終了する場合は、赤外線を検出しないようにすると、マウスの制御が戻ります。切断ボタンを押してから終了させてください。

解説

 Wiiリモコンの赤外線カメラの値を取得して、マウスカーソルの座標を制御し、Wiiリモコンのボタン制御をマウス制御として出力しています。

 

      //DLL読み込み用
      [DllImport("user32.dll")]
      extern static uint SendInput( uint nInputs, INPUT[] pInputs, int cbSize   );

      //DLL読み込み用
      [StructLayout(LayoutKind.Sequential)]
      struct INPUT {
         public int type;
         public MOUSEINPUT mi;
      }

      //DLL読み込み用
      [StructLayout(LayoutKind.Sequential)]
      struct MOUSEINPUT {
         public int dx;
         public int dy;
         public int mouseData;
         public int dwFlags;
         public int time;
         public IntPtr dwExtraInfo;
      }

 マウスの制御を行うために、user32.dllのSendInputを呼び出すための命令です。読み出しにDllimportを用いているのでunsing System.Runtime.InteropServicesを宣言しています。ここでは詳しく説明しませんので各自調べてみてください。

 

            //X座標を反転させる
            px = this.ScreenSize.X - px;
 前回のプログラムではX軸に関してはWiiリモコンの動作の反対の動きをしていました。右にWiiリモコンを振ると、マウスカーソルが左に動いたかと思います。画面解像度の幅から赤外線検出位置のX座標を引くことで、反転し、Wiiリモコンの動作と連動することができます。

 

            INPUT[] input = new INPUT[1];                       //マウスイベントを格納

               input[0].mi.dwFlags = 0x0002;                       //左マウスダウン
               SendInput(1, input, Marshal.SizeOf(input[0]));      //マウスイベントを送信
 Wiiリモコンのボタン動作を、マウス動作イベントとして送信しています。input[0].mi.dwFlagsにマウス動作を代入して送信しています。表に各イベントの値を示す。
動作意味
MOUSEEVENTF_MOVEマウスが移動0x0001
MOUSEEVENTF_LEFTDOWN左ボタンが押された0x0002
MOUSEEVENTF_LEFTUP左ボタンが離された0x0004
MOUSEEVENTF_RIGHTDOWN右ボタンが押された 0x0008
MOUSEEVENTF_RIGHTUP右ボタンが離された0x0010
MOUSEEVENTF_MIDDLEDOWN中央ボタンが押された0x0020
MOUSEEVENTF_MIDDLEUP中央ボタンが離された0x0040
MOUSEEVENTF_ABSOLUTE正規化された絶対座標0x8000

 

 前回のプログラムと比べるとコード数が長くなりましたが、行っていることは非常に単純です。Wiiリモコンのボタン動作をマウス動作として出力を行っています。マウスのクリック動作を見てみると、ボタンをクリック(DOWN)し、ボタンを離す(UP)。この2つの動作が行われます。この2つの動作を検出し制御を行っています。