広告あり

【DXライブラリ】ゲームパッドで操作できるようにするその1

DXライブラリとC言語でゲーム制作その6。今回はゲームパッドで操作できるようにする方法について。XInputで3Dゲーム(=左右のスティックを使う)を作る前提です。

ゲームパッドで操作できるようにしたい

PCゲームの場合キーボードで操作できるようにするのが基本だがやはりゲームパッドで操作できるようにしたいところ(タイピングゲームとかなら別)。

ゲームパッドにはDirectInputとXInputという2つの形式があるが今回はXInputの方を採用。理由はDirectInputだと各社によってゲームパッドの仕様が異なりやりにくいため(特に右スティックが曲者)。
XInputは主にXBOXで使われるゲームパッドの形式だったが近年ではPCゲーム(特にSteam)で推奨されることが多い。ちなみにNintendo Switchで使われているのはDirectInput。

XInput対応のゲームパッドなんてないよ、という人はこちら↓

あと3Dゲーム(=左右のスティックを両方使う)で操作するのを前提にしてます。

スポンサーリンク

実装

さっそく実装。以前作ったInput.cpp/hに以下のコードを追加。

Input.h

//以下の変数・関数を追加
const static int XINPUT_LEFTTRIGGER = 16;
const static int XINPUT_RIGHTTRIGGER = 17;
const static int XINPUT_THUNMBL_UP = 18;
const static int XINPUT_THUNMBL_DOWN = 19;
const static int XINPUT_THUNMBL_LEFT = 20;
const static int XINPUT_THUNMBL_RIGHT = 21;
const static int XINPUT_THUNMBR_UP = 22;
const static int XINPUT_THUNMBR_DOWN = 23;
const static int XINPUT_THUNMBR_LEFT = 24;
const static int XINPUT_THUNMBR_RIGHT = 25;

void Input_UpdateGanepad();
bool Input_GetGamepadDown(int KeyCode);
int Input_GetGamepad(int KeyCode);
bool Input_GetGamepadUp(int KeyCode);
void Input_AllUpdate();

Input.cpp

//以下の変数・関数を追加
const static int GAMEPAD_MAX = 25;          //ゲームパッドのボタンおよびスティックの総数
const static int TRIGGER_DEADZONE = 127;    //トリガーの無効範囲(この値までを無効)
const static int STICK_DEADZONE = 12000;     //スティックの無効範囲(この値までを無効)

static int Input_Gamepad[GAMEPAD_MAX];      //ゲームパッドが押されているフレーム数を格納する

//ゲームパッドの入力状態を更新する
void Input_UpdateGanepad() {
    int i;

    XINPUT_STATE input; //現在のゲームパッドの入力状態を格納する

    // 入力状態を取得
    GetJoypadXInputState(DX_INPUT_PAD1, &input);

    for (i = 0; i < GAMEPAD_MAX; i++) {
        if (i < 16) { //ボタン
            if (input.Buttons[i] != 0) { //i番に対応するボタンが押されていたら
                Input_Gamepad[i]++; //加算       
            }
            else {
                if (Input_Gamepad[i] > 0) { //前フレームで対応するキーが押されていれば
                    Input_Gamepad[i] = -1; //-1にする。-1はキーが離された時用の値
                }
                else {
                    Input_Gamepad[i] = 0; //0にする
                }
            }
        }
        else if (i == XINPUT_LEFTTRIGGER) { //Lトリガー
            if (input.LeftTrigger > TRIGGER_DEADZONE) { //Lトリガーが一定以上押し込まれていれば
                Input_Gamepad[i]++; //加算       
            }
            else {
                if (Input_Gamepad[i] > 0) { //前フレームで対応するキーが押されていれば
                    Input_Gamepad[i] = -1;  //-1にする。-1はキーが離された時用の値
                }
                else {
                    Input_Gamepad[i] = 0; //0にする
                }
            }
        }
        else if (i == XINPUT_RIGHTTRIGGER) { //Rトリガー
            if (input.RightTrigger > TRIGGER_DEADZONE) { //Rトリガーが一定以上押し込まれていれば
                Input_Gamepad[i]++; //加算       
            }
            else {
                if (Input_Gamepad[i] > 0) { //前フレームで対応するキーが押されていれば
                    Input_Gamepad[i] = -1;  //-1にする。-1はキーが離された時用の値
                }
                else {
                    Input_Gamepad[i] = 0;   //0にする
                }
            }
        }
        else if (i == XINPUT_THUNMBL_UP) { //左スティック
            if (input.ThumbLY > STICK_DEADZONE) { //左スティックが上に一定以上倒されていたら
                Input_Gamepad[i]++;         //加算       
            }
            else {
                if (Input_Gamepad[i] > 0) { //前フレームで対応するキーが押されていれば
                    Input_Gamepad[i] = -1;  //-1にする。-1はキーが離された時用の値
                }
                else {
                    Input_Gamepad[i] = 0;   //0にする
                }
            }
        }
        else if (i == XINPUT_THUNMBL_DOWN) { 
            if (input.ThumbLY < -STICK_DEADZONE) { //左スティックが下に一定以上倒されていたら
                Input_Gamepad[i]++;         //加算       
            }
            else {
                if (Input_Gamepad[i] > 0) { //前フレームで対応するキーが押されていれば
                    Input_Gamepad[i] = -1;  //-1にする。-1はキーが離された時用の値
                }
                else {
                    Input_Gamepad[i] = 0;   //0にする
                }
            }
        }
        else if (i == XINPUT_THUNMBL_LEFT) {
            if (input.ThumbLX < -STICK_DEADZONE) { //左スティックが左に一定以上倒されていたら
                Input_Gamepad[i]++;         //加算       
            }
            else {
                if (Input_Gamepad[i] > 0) { //前フレームで対応するキーが押されていれば
                    Input_Gamepad[i] = -1;  //-1にする。-1はキーが離された時用の値
                }
                else {
                    Input_Gamepad[i] = 0;   //0にする
                }
            }
        }
        else if (i == XINPUT_THUNMBL_RIGHT) {
            if (input.ThumbLX > STICK_DEADZONE) { //左スティックが右に一定以上倒されていたら
                Input_Gamepad[i]++;         //加算       
            }
            else {
                if (Input_Gamepad[i] > 0) { //前フレームで対応するキーが押されていれば
                    Input_Gamepad[i] = -1;  //-1にする。-1はキーが離された時用の値
                }
                else {
                    Input_Gamepad[i] = 0;   //0にする
                }
            }
        }
        else if (i == XINPUT_THUNMBR_UP) { //右スティック
            if (input.ThumbRY > STICK_DEADZONE) { //右スティックが上に一定以上倒されていたら
                Input_Gamepad[i]++;         //加算       
            }
            else {
                if (Input_Gamepad[i] > 0) { //前フレームで対応するキーが押されていれば
                    Input_Gamepad[i] = -1;  //-1にする。-1はキーが離された時用の値
                }
                else {
                    Input_Gamepad[i] = 0;   //0にする
                }
            }
        }
        else if (i == XINPUT_THUNMBR_DOWN) {
            if (input.ThumbRY < -STICK_DEADZONE) { //右スティックが下に一定以上倒されていたら
                Input_Gamepad[i]++;         //加算       
            }
            else {
                if (Input_Gamepad[i] > 0) { //前フレームで対応するキーが押されていれば
                    Input_Gamepad[i] = -1;  //-1にする。-1はキーが離された時用の値
                }
                else {
                    Input_Gamepad[i] = 0;   //0にする
                }
            }
        }
        else if (i == XINPUT_THUNMBR_LEFT) {
            if (input.ThumbRX < -STICK_DEADZONE) { //右スティックが左に一定以上倒されていたら
                Input_Gamepad[i]++;         //加算       
            }
            else {
                if (Input_Gamepad[i] > 0) { //前フレームで対応するキーが押されていれば
                    Input_Gamepad[i] = -1;  //-1にする。-1はキーが離された時用の値
                }
                else {
                    Input_Gamepad[i] = 0;   //0にする
                }
            }
        }
        else if (i == XINPUT_THUNMBR_RIGHT) {
            if (input.ThumbRX > STICK_DEADZONE) { //右スティックが右に一定以上倒されていたら
                Input_Gamepad[i]++;         //加算       
            }
            else {
                if (Input_Gamepad[i] > 0) { //前フレームで対応するキーが押されていれば
                    Input_Gamepad[i] = -1;  //-1にする。-1はキーが離された時用の値
                }
                else {
                    Input_Gamepad[i] = 0;   //0にする
                }
            }
        }
    }
}

//指定のゲームパッドのボタンが押された瞬間か調べる
bool Input_GetGamepadDown(int KeyCode) {
    if (KeyCode >= GAMEPAD_MAX) return false;

    if (Input_Gamepad[KeyCode] == 1) return true;
    return false;
}

//指定のゲームパッドのボタンが押されているか調べる。返り値は押されているフレーム数
int Input_GetGamepad(int KeyCode) {
    if (KeyCode >= GAMEPAD_MAX) return 0;

    return Input_Gamepad[KeyCode];
}

//指定のゲームパッドのボタンが離された瞬間か調べる
bool Input_GetGamepadUp(int KeyCode) {
    if (KeyCode >= GAMEPAD_MAX) return false;

    if (Input_Gamepad[KeyCode] == -1) return true;

    return false;
}

//全ての入力状態を更新する
void Input_AllUpdate() {
    //キーボード
    Input_UpdateKeyboard();

    //ゲームパッド
    Input_UpdateGanepad();
}

ゴリ押し感があるが気にしない。アナログ入力があるせい。

まず XINPUT_STATE input を定義して int GetJoypadXInputState() でボタンおよびスティックの生の情報を取得。あとはキーボードの時と同じく int Input_Gamepad[GAMEPAD_MAX] に押されているフレーム数を格納。あとはそのフレーム数で押された瞬間や離された瞬間を判定する。

トリガーおよびスティックの入力確認のときに使用している TRIGGER/STICK_DEADZONE は無効範囲の指定用。この値が小さいと誤判定されやすくなるのである程度大きめに。スティックの場合は SetJoypadDeadZone(int InputType, double Zone)で無効範囲を指定してもOK。

XINPUT_STATEや SetJoypadDeadZone についての詳細な情報はこちらのページにて。

Input.cpp/hにコードを追加したらあとは他のソースファイルのキー入力の更新・確認の部分を書き直していく。今回は System.cppのSystem_MainLoop() にあるキー入力更新の部分を以下のように変更。

//メインループ
bool System_MainLoop() {
    while (1) {
        //中略

        //全ての入力状態を更新(Input_UpdateKeyboard()から変更)
        Input_AllUpdate();

        //中略
    }

    return true;
}

Menu.cppの Menu_Update() もキー入力確認の部分を以下のように変更。

//更新
void Menu_Update() {
    if (Input_GetKeyboardDown(KEY_INPUT_DOWN) || Input_GetGamepadDown(XINPUT_BUTTON_DPAD_DOWN) || Input_GetGamepadDown(XINPUT_THUNMBL_DOWN)) {    //下キーが押されていたら
        NowSelect = (NowSelect + 1) % eMenu_Num;//選択状態を一つ下げる
    }
    if (Input_GetKeyboardDown(KEY_INPUT_UP) || Input_GetGamepadDown(XINPUT_BUTTON_DPAD_UP) || Input_GetGamepadDown(XINPUT_THUNMBL_UP)) {    //上キーが押されていたら
        NowSelect = (NowSelect + (eMenu_Num - 1)) % eMenu_Num;//選択状態を一つ上げる
    }

    if (Input_GetKeyboardDown(KEY_INPUT_RETURN) || Input_GetGamepadDown(XINPUT_BUTTON_B)) {  //エンターキーかBボタンが押されたら
        switch (NowSelect) {//現在選択中の状態によって処理を分岐
        case eMenu_Game://ゲーム選択中なら
            SceneMgr_ChangeScene(eScene_Game);//シーンをゲーム画面に変更
            break;
        case eMenu_Config://設定選択中なら
            SceneMgr_ChangeScene(eScene_Config);//シーンを設定画面に変更
            break;
        default:
            break;
        }
    }
}

あとは実行して十字キーの上下や左スティック、Bボタンを押して正しく処理されているかどうか確認する。

スポンサーリンク

補足

とりあえずキーボードと同じようにゲームパッドで操作できるようにしてみたが、実際はキーボードとゲームパッドの入力をマージしてそれで入力を判定するのが基本。この機能を作る際一度ゲームでの操作操作(移動・ジャンプ・ダッシュ・攻撃etc)をまとめたものが必要になる。キーコンフィグを実装するときも同様。

ちなみに3Dゲーム内でプレイヤーを移動させるときは今回実装したものとは違う方法で求める。

続きについては実装でき次第更新。

コメント

タイトルとURLをコピーしました