2016年4月29日金曜日

C#でジョイパッド入力

C#でジョイパッド入力の方法はいくつか有り、例えばXNAやDirectX SDKを使用する方法があります。しかしXNAはすでに開発が終了し、対応したジョイパッドはXbox360コントローラしかありません。後者の方法は情報も比較的多めですが、SDKの配布が終了?していたり、僕の環境では動かなかったりと、いろいろ問題が有ります。
ということで今回はDLLを使用して読み取ってみました。

とりあえず当初の目標であるXbox360以外のジョイスティックも動作確認しました。おそらくWindowsがゲームコントローラとして認識できるやつなら読み込めるはずです。

一応汎用的なClassと、Xbox360ゲームコントローラ向けのClass(汎用を継承)を用意しています。
注意点として、XNAではLeftShoulderとRightShoulderと、トリガー入力を左右で読み込めますが、DLLを叩いた場合は左右で1軸となり、両方のトリガーを引くと0となり、両方のトリガーを放した状態と区別できません。左右別々に引いた場合は正負で判断できるので、同時にトリガーが両方共引かれることのない操作方法を考える必要があります(例えばフライトシミュでトリガーをラダーに割当、ラダーを両方操作するとエアブレーキ みたいなUIは不可能です)。
その他はXNAと同様のデータを扱えます。もちろん真ん中の椎茸キーが使用できないのも同じです。

以下のクラスを使用している分には、ジョイパッドの素性を把握することはできません。そのコントローラがXbox360のコントローラなのか、別の製品なのか、ということだけでなく、そのジョイパッドに搭載されている軸数・ボタン数も把握できません。このクラスでは最大6軸、32ボタン、1POVまで対応できます。一応dwButtonNumberというそれらしい変数は有りますが、これはcurrent button number pressedと書かれている通り、現在押されているボタンの数を示しているに過ぎません。
一応このクラスを拡張してXbox360に特化したり、他のジョイパッドに特化したりもできますが、必ずしも対応したジョイパッドが接続されているとは限らないことに注意して下さい。

それとアナログスティックについてですが、基本的にXbox360のリターンスプリングは信用できません。-1から+1の範囲に正規化した場合、スティックのセンターが0.25当たりになる場合があります。±0.3未満は読み捨てるか、OverGのように不感帯を調整できるようにするか、もしくはBF3のように気にせずにセンターでも移動するか、いずれかを選択する必要があります。大抵のジョイスティックは±200も暴れることはなく、もっと狭い範囲に収まると思いますが、それでもセンターが0になるとは限りません。

POVの単位はmilli degreeです。上が0、右が90000、下が180000、左が270000で、右上などの中間はそれぞれの中間値となります。POVが入力されていない場合は65535となります。定数で上下左右は判断できますが、中間値を判断する場合は自分で値を書く必要があります(もしくは中間値の定数を追加しましょう)。


参考にしたページ等
ゲームパッド(ジョイパッドと言うらしい)を使ってみる
 ゲームパッド読み取り全般

構造体のポインタを引数に受け取る関数の呼び出し - はらぐろブログラマン
 C#でDLLを叩く方法とか

XNA Game Studioで始めるゲームプログラミング(ISBN:9784048700283)
 Xbox360ゲームコントローラのスイッチ名とか

Windowsに入ってるmmsyscom.h
 構造体の中身他

2016年4月1日金曜日

C#のGraphicsのTransform

GraphicsのTransformで座標変換を行う。

例えば
gra.Clear(Color.DarkGreen);
gra.DrawLine(Pens.Lime, -50, -50, 50, 50);
gra.DrawLine(Pens.Lime, -50, 50, 50, -50);
というコードが合った場合、結果は以下のようになる。



本来はクロスを書いているのだが、負の座標は表示されない。しかし座標を負の位置でも扱い、Graphicsの方で座標変換をしてくれると助かる場合がある。ということでなんとかしてみる。

double deg = 45;
float scale = 1.00f;
float offsetX = 50;
float offsetY = 50;

double rad = deg * Math.PI / 180;
float sin = scale * (float)Math.Sin(rad);
float cos = scale * (float)Math.Cos(rad);

System.Drawing.Drawing2D.Matrix mtx = new System.Drawing.Drawing2D.Matrix(
    cos, sin,
    -sin, cos,
    offsetX, offsetY);

gra.Transform = mtx;

結果



CW方向の角度をdegreesで指定する。またscaleを指定することにより0.5fなら2分の1のサイズとなる。scaleを指定した場合でも、offsetは1/1で使用される。敵キャラのアイコンをX100,Y200の位置に5分の1の大きさで表示したいなら、offX=100; offY=200; scale=0.2f;のように指定すればいい。敵キャラを自キャラに向けたい場合は相対位置をatan2で角度に変換して渡せば自分の方を向くことになる。

Transformを使う注意点としては、上記のコードでは位置や角度は絶対位置で指定する点。なので自キャラの位置を表示するために座標を画面の中心に移動し、敵キャラを自キャラからの相対位置で指定する、といったことをするにはもう一工夫が必要となる。