C#でゲームパッド入力

Windowsではゲームパッド入力はDirectXを介するのでキーボード入力ほど簡単には扱えない。C#アプリでゲームパッド入力を取るにはDirectXをラップしたなんらかの.NETライブラリが必要になる。このようなライブラリには有名なものとして SlimDX と SharpDX がある。SlimDX は2012年を最後にアップデートされておらず、DirectX 11までの対応となっている。SharpDX も残念ながら2019年に開発終了となってしまった。代替手段が見つかるまで当面は SharpDX を使うことにする。

NuGetでSharpDX.DirectInputをインストール

DirectInputDirectXの一部であり、マウス、キーボード、ゲームパッド等の入力を得るためのAPIである。SharpDX.DirectInputはこれをラップした.NETライブラリである。

「プロジェクト」→「NuGetパッケージの管理」で「DirectDX.DirectInput」を検索し、プロジェクトにインストールする。

また、C#ソースの冒頭に下記のusingを追加する。

using SharpDX;
using SharpDX.DirectInput;

変数の定義

// DirectInput
DirectInput dinput = new DirectInput();
// ゲームパッド
Joystick joystick = null;
// 使用するゲームパッドのID
Guid joystickGuid = Guid.Empty;

ゲームパッドを探す

ゲームパッドバイスまたはジョイスティックデバイスの中から最初に見つかったもののIDを取得する。

// ゲームパッドを探す
if (joystickGuid == Guid.Empty)
{
    foreach (DeviceInstance device in 
      dinput.GetDevices(DeviceType.Gamepad, DeviceEnumerationFlags.AllDevices))
    {
        joystickGuid = device.InstanceGuid;
        break;
    }
}
// ジョイスティックを探す
if (joystickGuid == Guid.Empty)
{
    foreach (DeviceInstance device in
      dinput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AllDevices))
    {
        joystickGuid = device.InstanceGuid;
        break;
    }
}

ゲームパッドの初期化

// 見つかった場合
if (joystickGuid != Guid.Empty)
{
    // ゲームパッドの取得
    joystick?.Dispose();
    joystick = new Joystick(dinput, joystickGuid);
    // ゲームパッドが取得できた場合
    if (joystick != null)
    {
        // バッファサイズを指定
        joystick.Properties.BufferSize = 128;

        // ジョイスティックの軸の最小値と最大値を -1000~+1000に設定
        foreach (DeviceObjectInstance deviceObject in joystick.GetObjects())
        {
            switch (deviceObject.ObjectId.Flags)
            {
                // 絶対軸または相対軸
                case DeviceObjectTypeFlags.Axis:
                case DeviceObjectTypeFlags.AbsoluteAxis:
                case DeviceObjectTypeFlags.RelativeAxis:
                    var ir = joystick.GetObjectPropertiesById(deviceObject.ObjectId);
                    if (ir != null)
                    {
                        try
                        {
                            ir.Range = new InputRange(-1000, 1000);
                        }
                        catch (Exception) { }
                    }
                    break;
            }
        }
    }
}

ゲームパッドの入力を取得

ゲームパッドの入力を取得する。

// ゲームパッドの入力を取得
try{
    joystick.Acquire();
    joystick.Poll();
}catch{
    // ゲームパッドが抜けた
    joystick?.Dispose();
    joystick = null;
    return;
}
// ゲームパッドのデータ取得
var jState = joystick.GetCurrentState();
// 取得できない場合
if (jState == null) { return; }
ボタン入力の判定

ボタン1,2,3...に対応する添え字が0,1,2...であることに注意する。
また、ボタン番号とボタンの対応はゲームパッド製品によってバラバラなのでなんらかのテーブル(配列変数)で対応付けを管理する。

// ボタン1が押された場合
if(jState.Buttons[0]){ }
十字ボタン入力の取得

十字ボタンはハットスイッチ(POV、視点の角度切替え)として扱われる場合と、X軸/Y軸として扱われる場合があり、製品によって異なる。

// POVの場合
if (jState.PointOfViewControllers[0] == 0){}     // 上
if (jState.PointOfViewControllers[0] == 9000){}  // 右
if (jState.PointOfViewControllers[0] == 18000){} // 下
if (jState.PointOfViewControllers[0] == 27000){} // 左
// X軸/Y軸の場合
if (jState.X < -500){} // 左
if (jState.X >  500){} // 右
if (jState.Y < -500){} // 上
if (jState.Y >  500){} // 下
アナログスティック入力の取得

一般的なゲームパッドにはアナログスティックが2本つまり4軸あるが、DirectInputが対応しているアナログ入力はX,Y,Z軸、X,Y,Z軸の回転、POVなどであり、現実のゲームパッドとうまく対応しない。軸の割り当てはこれまた製品によってまちまちである。下記に一例を示す。

// 左のアナログスティックはX軸とY軸
int x1 = jState.X;
int y1 = jState.Y;
// 右のアナログスティックはZ軸とZ軸の回転
int x2 = jState.RotationZ;
int y2 = jState.Z;

参考ページ

csdegame.net