---
ゲームパッド (Joystick? Joypad?)を読むにはDirectInputが定番らしくて、たしかに簡単そうに見えたのでチャレンジしてみたのだけど、managed DirectXを.NET 4で使うにはapp.configを書き替えないといけないとか、SetCooperativeLevelの第一引数がWindows.Windows.Forms.Controlだったりする罠(HWNDも取れるので実は後述の方法でいけるのだけど)にあったりして、挫折した。
ゲームパッドを読むためのAPIがWin32のwinmmというのにあるらしいとわかったが、P/Invokeの定義を書くのが面倒なので探してみたら、Tao Frameworkというゲーム用のフレームワークに定義されているらしい。調べてみると、MITライセンスなので使うことにした。
簡単な使い方
Joystick系のAPIはTao.Platform.Windowsに入っている。Tao Frameworkにはインストーラもあるのだけど、このdllを参照追加すればいいだけなので、zipを落としてdllだけ横に置くのが簡単でいいと思う。参照追加したら、JOYINFO構造体をnewして、joyGetPos()というメソッドにjoypad IDをセットで渡せばおわり。IDはただの0からの連番。なお、joyGetNumDevs()は、つながっているデバイスの数を返すわけじゃないから注意だ。
for (int i = 0; i < Tao.Platform.Windows.Winmm.joyGetNumDevs(); i++) { Tao.Platform.Windows.Winmm.JOYINFO joyinfo = new Tao.Platform.Windows.Winmm.JOYINFO(); if (Tao.Platform.Windows.Winmm.joyGetPos(i, ref joyinfo) == Tao.Platform.Windows.Winmm.JOYERR_NOERROR) { this.textBlock1.Text += String.Format("Joypad[{0}] detected. Button: {1}\n", i, joyinfo.wButtons); } }パッドによってはもっと詳しい情報が取れるjoyGetPosEx()もあるらしい。自分の用途にはその必要がなかったので使わなかったけど、このへんのAPIの使い方についてはこのへんに書いてある。
イベント的に読み取る1: タイマーで読み取る
もうちょっと実用的に使うために、押されたことを検知したい。API的には、joySetCapture()というメソッドがあり、これを使うと、ウィンドウメッセージを飛ばすことができる。しかし、引数からしてポーリングっぽい感じなので、WPFアプリケーションであれば、HWNDとか意識しなくていいので、タイマーで見たほうが素直だと思うのでそうした。実装はこのような感じ。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private void Window_ContentRendered(object sender, EventArgs e) | |
{ | |
Tao.Platform.Windows.Winmm.JOYINFO joyinfo = new Tao.Platform.Windows.Winmm.JOYINFO(); | |
int lastJoystick = -1; | |
for (int i = 0; i < Tao.Platform.Windows.Winmm.joyGetNumDevs(); i++) | |
{ | |
if (Tao.Platform.Windows.Winmm.joyGetPos(i, ref joyinfo) == Tao.Platform.Windows.Winmm.JOYERR_NOERROR) | |
{ | |
lastJoystick = i; | |
} | |
} | |
if (lastJoystick < 0) | |
{ | |
this.textBlock1.Text = "Joystick not found.\n"; | |
return; | |
} | |
DispatcherTimer timer = new DispatcherTimer(DispatcherPriority.Normal); | |
timer.Interval = new TimeSpan(0, 0, 0, 0, 10); | |
bool downState = false; | |
timer.Tick += new EventHandler((sender1, e1) => | |
{ | |
if (Tao.Platform.Windows.Winmm.joyGetPos(lastJoystick, ref joyinfo) == 0) | |
{ | |
bool nowDown = (joyinfo.wButtons & Tao.Platform.Windows.Winmm.JOY_BUTTON1) != 0; | |
if (downState && !nowDown) | |
{ | |
this.textBlock1.Text += "Button 1 UP\n"; | |
downState = false; | |
} | |
else if (!downState && nowDown) | |
{ | |
this.textBlock1.Text += "Button 1 DOWN\n"; | |
downState = true; | |
} | |
} | |
}); | |
timer.Start(); | |
} |
イベント的読み取る2: joySetCaptureで読み取る
それでもjoySetCaptureが使いたいならば、こんなかんじ。 一応動いた。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private void Window_ContentRendered(object sender, EventArgs e) | |
{ | |
IntPtr hwnd = new WindowInteropHelper(this).Handle; | |
HwndSource source = HwndSource.FromHwnd(hwnd); | |
source.AddHook(new HwndSourceHook(WndProc)); | |
int lastjoystickid = 0; // FIXME: set properly | |
// 1st argument is int... | |
Tao.Platform.Windows.Winmm.joySetCapture(hwnd.ToInt32(), lastjoystickid, 100, true); | |
} | |
// MM_JOY1MOVE, MM_JOY1ZMOVE, MM_JOY2MOVE, MM_JOY2ZMOVE are defined in Tao.Platform.Windows.Winmm. | |
private const int MM_JOY1BUTTONDOWN = 0x3B5; | |
private const int MM_JOY2BUTTONDOWN = 0x3B6; | |
private const int MM_JOY1BUTTONUP = 0x3B7; | |
private const int MM_JOY2BUTTONUP = 0x3B8; | |
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) | |
{ | |
if (msg == MM_JOY1BUTTONDOWN) | |
{ | |
Dispatcher.Invoke(new System.Action(() => | |
{ | |
textBlock1.Text += "Button down!\n"; | |
})); | |
} | |
return IntPtr.Zero; | |
} |
0 件のコメント:
コメントを投稿