2013/10/30

とりあえずJoystick

Kです。テスト用プログラムにJoystickを使いたいので、C#からDirect Inputを使用してボタンの判定を行うクラスを作ってみました。


動作との関連づけは、各デバイスクラスにonbuttonメソッドがあり、全てのコントローラからのデータはこいつが受け取ります。設定により登録されたデバイスに対して、登録されたボタンが押されたりするとそのボタンと任意の文字列を渡す仕組みです。アナログスティックについては、常に通知する仕組みです。

joystick.setButtons(new Byte[] { 1, 3, 2, 0 }, 4);
によってキー割り当てを変更しています。
これは、左が1, 上が2, 下が3, 右が4となっているコントローラにおいて、
上から時計回りに0, 1, 2, 3と割り当てるための記述です。
また、上記4つのボタン以外は受け付けません。


using System;
using System;
using IWshRuntimeLibrary;
using Microsoft.DirectX;
using Microsoft.DirectX.DirectInput;
using System.Windows.Forms;

using System.Collections.Generic;

class Form1 : Form {

}

class Program {
   public static void Main() {
      Device device;
      Joystick joystick;
      Form1 form;

      form = new Form1();

      device = serachDevice(form.Handle);

      if(device != null) {
         Joystick.Command jc;

         System.Console.WriteLine("デバイス見付かった");
         joystick = new Joystick(device);

         joystick.setButtons(new Byte[] { 1, 3, 2, 0 }, 4);

         jc = new Joystick.Command(0, "△");
         jc.onbutton += onButton;
         joystick.setCommand(jc);

         jc = new Joystick.Command(1, "○");
         jc.onbutton += onButton;
         joystick.setCommand(jc);

         jc = new Joystick.Command(2, "×");
         jc.onbutton += onButton;
         joystick.setCommand(jc);

         jc = new Joystick.Command(3, "□");
         jc.onbutton += onButton;
         joystick.setCommand(jc);
      }
      Application.Run(form);
   }

   private static void onButton(int no, string message) {
      System.Console.WriteLine("ID={0} / mes={1}", no, message);
   }

   private static Device serachDevice(System.IntPtr hwnd) {
      DeviceList dl;
      Device joystick = null;

      dl = Manager.GetDevices(DeviceClass.GameControl, EnumDevicesFlags.AttachedOnly);

      foreach(DeviceInstance dev in dl) {
         joystick = new Device(dev.InstanceGuid);
         joystick.SetCooperativeLevel(hwnd, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);

         // 最初に見つかったジョイスティックを操作対象とする
         break;
      }

      if(joystick != null) {
         joystick.Acquire();
      }

      return joystick;
   }
}

/*
 * ### ゲーミング用Joystickクラス ###
 * 特定のコマンドを検出したら、登録されたデリゲートに通知する。
 * setButtons(int[] )で、
 */
class Joystick {
   // pulibc
   public delegate void onButton(int no, string message);

   public Joystick(Device joystick) {
      this.joystick = joystick;

      // タイマーの作成・起動
      var mytimer = new System.Threading.Timer(new System.Threading.TimerCallback(onTimer),
         this, 0, 10);

      // ボタンルール・ボタンカウントの初期設定
      button_rule = new byte[128];
      button_number = 0;

      button_counter = new int[128];
      button_repeat_counter = new int[128];

      // コマンドリストの作成
      commands = new List<Command>();
   }

   public void setButtons(byte[] button_rule, int button_number) {
      button_rule.CopyTo(this.button_rule, 0);

      this.button_number = button_number;
   }

   public void setCommand(Command command) {
      commands.Add(command);
   }

   // private
   private Device joystick;
   private int button_number;
   private byte[] button_rule;
   private int[] button_counter, button_repeat_counter;
   private List<Command> commands;

   private static void onTimer(object sender) {
      Joystick joystick = (Joystick)sender;

      // ポーリング
      joystick.joystick.Poll();
      JoystickState js = joystick.joystick.CurrentJoystickState;

      // ボタンの検出
      byte[] buttons = js.GetButtons();

      // ここでのiは再定義した仮想番号
      // 仮想番号→実Joystick番号に変換した後データを受け取る
      for(int i = 0; i < joystick.button_number; i++){
         if(buttons[joystick.button_rule[i]] == 0x80) {
            // 通常単押しまでカウント
            if(joystick.button_counter[i] >= 5) {
               // 長押しの検出
               if(joystick.button_repeat_counter[i] > 1) {
                  foreach(Command command in joystick.commands) {
                     if(command.checkRepeatCommand(i)) {
                        command.onbutton(i, command.message);
                     }
                  }

                  joystick.button_repeat_counter[i] = 0;
               }
               else {
                  joystick.button_repeat_counter[i]++;
               }

               // 1回目だけ単押し
               if(joystick.button_counter[i] == 5) {
                  foreach(Command command in joystick.commands) {
                     if(command.checkCommand(i)) {
                        command.onbutton(i, command.message);
                     }
                  }
               }
            }

            joystick.button_counter[i]++;
         }
         else {
            joystick.button_counter[i] = 0;
            joystick.button_repeat_counter[i] = 0;
         }
      }
   }

   // 仮想番号を登録または、アナログスティックの登録
   public class Command {
      // public
      public onButton onbutton;
      public string message;

      public enum analog_direction {
         RX, RY, LX, LY, ZX, ZY, NONE
      };

      public Command(int no, string message) {
         this.no = no;
         this.is_repeat = false;
         this.ad = analog_direction.NONE;
         this.message = message;
      }

      public Command(int no, bool is_repeat, string message) {
         this.no = no;
         this.is_repeat = is_repeat;
         this.ad = analog_direction.NONE;
         this.message = message;
      }

      public Command(analog_direction ad, string message) {
         this.no = 0;
         this.is_repeat = false;
         this.ad = ad;
         this.message = message;
      }

      public bool checkCommand(int no) {
         // アナログスティックの場合は無条件
         if(ad != analog_direction.NONE) {
            return true;
         }
         
         // コマンドの照会
         return this.no == no;
      }

      public bool checkRepeatCommand(int no) {
         // コマンドの照会
         return this.no == no && is_repeat;
      }

      // private
      private int no;
      private bool is_repeat;
      private analog_direction ad;
   }
}

0 件のコメント:

コメントを投稿