LoginSignup
2
2

More than 5 years have passed since last update.

XInput + x360ceの使用

Posted at

XInputの特徴とか

  • 基本XBox 360のコントローラ用のライブラリ
  • キーボードやマウスを取る場合はDirectInputやWin32APIを用いる
  • デバイスが限定されている分、DirectInputと比べてかなり簡単に扱える
  • XInputに対応してないコントローラもx360ceを用いることで使えるようになる

x360ce

x360ce - XBOX 360 Controller emulator
XInput対応コントローラを持っていないため確認できないが、パススルーの設定もできるためDirectInput対応コントローラとXInput対応コントローラの共存は可能な模様

使用方法

  • x360ce Librariesと書かれたものダウンロードして中のxinput1_3.dllを実行ファイルから見えるディレクトリに配置
  • XBOX 360 Controller emulatorと書かれたものをダウンロードして中のx360ce.exeを適当な場所に配置・起動してコントローラの設定を行い、作成されたx360ce.iniをxinput1_3.dllと同じディレクトリに配置

注意点

  • x360ce.exeから自動作成されるxinput1_3.dllをx360ce Librariesの代わりに用いると、デバッグ時にエラーを吐く
  • x360ce.ini中のInstanceGuidはPC毎にも変わるため、環境ごとに編集する必要あり
  • バイブレーションは対応してなさそう

ラッパークラス

作ったものの正直必要ない気はする

Joypad.h
#pragma once
#include <complex>

#ifndef STRICT
#define STRICT
#endif

#include <windows.h>
#include <XInput.h>

inline void JoypadEnable(bool enable) { XInputEnable(enable); }

class Joypad{
public:
    enum class Button{
        A = XINPUT_GAMEPAD_A,
        B = XINPUT_GAMEPAD_B,
        X = XINPUT_GAMEPAD_X,
        Y = XINPUT_GAMEPAD_Y,
        Up = XINPUT_GAMEPAD_DPAD_UP,
        Down = XINPUT_GAMEPAD_DPAD_DOWN,
        Left = XINPUT_GAMEPAD_DPAD_LEFT,
        Right = XINPUT_GAMEPAD_DPAD_RIGHT,
        Start = XINPUT_GAMEPAD_START,
        Back = XINPUT_GAMEPAD_BACK,
        LThumb = XINPUT_GAMEPAD_LEFT_THUMB,
        RThumb = XINPUT_GAMEPAD_RIGHT_THUMB,
        LShoulder = XINPUT_GAMEPAD_LEFT_SHOULDER,
        RShoulder = XINPUT_GAMEPAD_RIGHT_SHOULDER
    };

    Joypad(int userIndex) : m_UserIndex(userIndex) {}

    bool update();
    bool isPushed(Button button);
    bool vibrate(double leftMotorSpeedByPercent, double rightMotorSpeedByPercent);
    bool isConnected();
    double getLTrigger(double thresholdByPercent = XINPUT_GAMEPAD_TRIGGER_THRESHOLD / (double)TriggerMax);
    double getRTrigger(double thresholdByPercent = XINPUT_GAMEPAD_TRIGGER_THRESHOLD / (double)TriggerMax);
    std::complex<double> getLStick(double thresholdByPercent = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE / (double)StickMax);
    std::complex<double> getRStick(double thresholdByPercent = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE / (double)StickMax);
    int getUserIndex();
private:
    static const long TriggerMax = 255;
    static const long MotorSpeedMax = 65535;
    static const long StickMax = 32767;

    int m_UserIndex;
    XINPUT_STATE m_State;
    bool m_Connected;
};
Joypad.cpp
#include "Joypad.h"

#include <algorithm>
#pragma comment(lib, "Xinput.lib")



bool Joypad::isPushed(Button button) {
    return isConnected() && (m_State.Gamepad.wButtons & static_cast<int>(button));
}

bool Joypad::isConnected() {
    return m_Connected;
}

bool Joypad::update() {
    m_Connected = (XInputGetState(m_UserIndex, &m_State) == ERROR_SUCCESS);
    return m_Connected;
}

int Joypad::getUserIndex() {
    return m_UserIndex;
}

std::complex<double> Joypad::getLStick(double thresholdByPercent) {
    if (!isConnected()) return std::complex<double>(0, 0);
    auto y_abs = std::abs(m_State.Gamepad.sThumbLY) / (double)StickMax; // 負の割り算は処理系定義な為
    auto y_sign = (m_State.Gamepad.sThumbLY < 0) ? -1 : 1;
    auto x_abs = std::abs(m_State.Gamepad.sThumbLX) / (double)StickMax;
    auto x_sign = (m_State.Gamepad.sThumbLX < 0) ? -1 : 1;

    return std::complex<double>(
        (x_abs < thresholdByPercent) ? 0 : x_abs * x_sign,
        (y_abs < thresholdByPercent) ? 0 : y_abs * y_sign
        );
}

std::complex<double> Joypad::getRStick(double thresholdByPercent) {
    if (!isConnected()) return std::complex<double>(0, 0);
    auto y_abs = std::abs(m_State.Gamepad.sThumbRY) / (double)StickMax;
    auto y_sign = (m_State.Gamepad.sThumbRY < 0) ? -1 : 1;
    auto x_abs = std::abs(m_State.Gamepad.sThumbRX) / (double)StickMax;
    auto x_sign = (m_State.Gamepad.sThumbRX < 0) ? -1 : 1;

    return std::complex<double>(
        (x_abs < thresholdByPercent) ? 0 : x_abs * x_sign,
        (y_abs < thresholdByPercent) ? 0 : y_abs * y_sign
    );
}

double Joypad::getLTrigger(double thresholdByPercent) {
    if (!isConnected()) return 0;
    auto t = m_State.Gamepad.bLeftTrigger / (double)TriggerMax;
    return (t < thresholdByPercent) ? 0 : t;
}

double Joypad::getRTrigger(double thresholdByPercent) {
    if (!isConnected()) return 0;
    auto t = m_State.Gamepad.bLeftTrigger / (double)TriggerMax;
    return (t < thresholdByPercent) ? 0 : t;
}

bool Joypad::vibrate(double leftMotorSpeedByPercent, double rightMotorSpeedByPercent) {
    XINPUT_VIBRATION vib;
    vib.wLeftMotorSpeed = leftMotorSpeedByPercent * MotorSpeedMax;
    vib.wRightMotorSpeed = rightMotorSpeedByPercent * MotorSpeedMax;
    return XInputSetState(m_UserIndex, &vib) == ERROR_SUCCESS;
}
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2