ジョイパッドの状態を取得(DirectInput)

ウインドウハンドルを取得して DirectInput を使用します。

『ウインドウハンドルさえ取得できれば、何でもできる!!』(アントニオ猪木風)

冗談はさておき、ウインドウハンドルが取得できれば freeglut と DirectInput を
組み合わせて使用する事ができます。
今回のプログラムは青い四角形をジョイパッドの方向キーで移動できます。
DirectInput に対応しているジョイパッドが無いと動作しません。
DirectInput は DirectX8 で更新が停止しており、今後は XInput の使用を
マイクロソフトが推奨しています。

*(2014.3.28追記)
尚、Visual Studio 2010からはディレクトリの参照設定を
プロジェクト毎に個別に設定しないといけないようになってしまいました。
面倒くさいですね。(^ω^;)
プロジェクトのプロパティで「C/C++」の「追加のインクルードディレクトリ」に
$(DXSDK_DIR)Include を追加するとビルドが通るようです。

ファイル
main.cpp

main.cpp

#pragma comment(linker, "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
#pragma comment(lib,"dinput8.lib")
#pragma comment(lib,"dxguid.lib")

#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>

#include <windows.h>
#include <GL/freeglut/freeglut.h>

#define WIDTH 320
#define HEIGHT 240

int x=150,y=80;

#define RELEASE(x) {if(x){(x)->Release();x=NULL;}}//安全に解放する
#define Range  1000
#define Threshold   Range/4 //しきい値

// DirectInputの変数
LPDIRECTINPUT8 DInput = NULL;     //DirectInput
LPDIRECTINPUTDEVICE8 DIDevice = NULL;   //DirectInputデバイス
DIDEVCAPS DevCaps;        //ジョイスティックの能力
#define DIDEVICE_BUFFERSIZE 100     //デバイスに設定するバッファ・サイズ
HWND hWnd2=NULL;        //ウィンドウハンドル
HINSTANCE hinstance;       //インスタンスのハンドル
DIJOYSTATE2 JOYPAD;        //ジョイパッドデータ


//ジョイスティックを列挙する関数
BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext)
{
 // 列挙されたジョイスティックへのインターフェイスを取得する。
 HRESULT hr = DInput->CreateDevice(pdidInstance->guidInstance, &DIDevice, NULL);
 if (FAILED(hr))return DIENUM_CONTINUE;

 // ジョイスティックの能力を調べる
 DevCaps.dwSize = sizeof(DIDEVCAPS);
 hr = DIDevice->GetCapabilities(&DevCaps);
 if (FAILED(hr)){
  // ジョイスティック能力の取得に失敗
  RELEASE(DIDevice);
  return DIENUM_CONTINUE;
 }
 return DIENUM_STOP;
}

//ジョイスティックの軸を列挙する関数
BOOL CALLBACK EnumAxesCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef)
{
 //軸の値の範囲を設定(-1000~1000)
 DIPROPRANGE diprg;
 ZeroMemory(&diprg, sizeof(diprg));
 diprg.diph.dwSize = sizeof(diprg);
 diprg.diph.dwHeaderSize = sizeof(diprg.diph);
 diprg.diph.dwObj = lpddoi->dwType;
 diprg.diph.dwHow = DIPH_BYID;
 diprg.lMin = -Range;
 diprg.lMax = +Range;
 HRESULT hr = DIDevice->SetProperty(DIPROP_RANGE, &diprg.diph);
 if (FAILED(hr))return DIENUM_STOP;

 return DIENUM_CONTINUE;
}
//ウィンドウを検索してHWNDとHINSTANCEを得る
BOOL CALLBACK enumWindowsProc(HWND hWnd,LPARAM lParam)
{
 HANDLE hModule=(HANDLE)GetWindowLong(hWnd,GWL_HINSTANCE);

 if(GetModuleHandle(NULL)==hModule){
  wchar_t ClassName[256];
  GetClassNameW(hWnd,ClassName,sizeof(ClassName)/sizeof(ClassName[0]));
  if(wcsncmp(ClassName,L"FREEGLUT",wcslen(ClassName))==0){
   hWnd2=hWnd;
   hinstance=(HINSTANCE)GetWindowLong(hWnd,GWL_HINSTANCE);
   return FALSE;
  }
 }
 return TRUE;
}
//DirectInputの初期化
bool Init_DirectInput(void)
{
 //DirectInputの作成
 HRESULT hr = DirectInput8Create(hinstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&DInput, NULL);
 if (FAILED(hr)){
  MessageBoxW(hWnd2,L"DirectInput8オブジェクトの作成に失敗",L"ERROR !!",MB_OK);
  return false;
 }

 //デバイスを列挙して作成
 hr = DInput->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, NULL, DIEDFL_ATTACHEDONLY);
 if (FAILED(hr) || DIDevice==NULL){
  MessageBoxW(hWnd2,L"DirectInputDevice8オブジェクトの作成に失敗",L"ERROR !!",MB_OK);
  return false;
 }
 
 //データ形式を設定
 hr = DIDevice->SetDataFormat(&c_dfDIJoystick2);
 if (FAILED(hr)){
  MessageBoxW(hWnd2,L"c_dfDIJoystick2形式の設定に失敗",L"ERROR !!",MB_OK);
  return false;
 }

 //モードを設定(フォアグラウンド&非排他モード)
 hr = DIDevice->SetCooperativeLevel(hWnd2, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
 if (FAILED(hr)){
  MessageBoxW(hWnd2,L"フォアグラウンド&非排他モードの設定に失敗",L"ERROR !!",MB_OK);
  return false;
 }

 //コールバック関数を使って各軸のモードを設定
 hr = DIDevice->EnumObjects(EnumAxesCallback, NULL, DIDFT_AXIS);
 if (FAILED(hr)){
  MessageBoxW(hWnd2,L"軸モードの設定に失敗",L"ERROR !!",MB_OK);
  return false;
 }

 //軸モードを設定(絶対値モードに設定。デフォルトなので必ずしも設定は必要ない)
 DIPROPDWORD diprop;
 diprop.diph.dwSize = sizeof(diprop);
 diprop.diph.dwHeaderSize = sizeof(diprop.diph);
 diprop.diph.dwObj = 0;
 diprop.diph.dwHow = DIPH_DEVICE;
 diprop.dwData  = DIPROPAXISMODE_ABS;
 hr = DIDevice->SetProperty(DIPROP_AXISMODE, &diprop.diph);
 if (FAILED(hr)){
  MessageBoxW(hWnd2,L"軸モードの設定に失敗",L"ERROR !!",MB_OK);
  return false;
 }

 //バッファリング・データを取得するため、バッファ・サイズを設定
 diprop.dwData = DIDEVICE_BUFFERSIZE;
 hr = DIDevice->SetProperty(DIPROP_BUFFERSIZE, &diprop.diph);
 if (FAILED(hr)){
  MessageBoxW(hWnd2,L"バッファ・サイズの設定に失敗",L"ERROR !!",MB_OK);
  return false;
 }

 //入力制御開始
 DIDevice->Acquire();

 return true;
}

void SquareFill2D(int x1,int y1,int x2, int y2){
 glBegin(GL_QUADS);
 glVertex2i(x1,y1);
 glVertex2i(x2,y1);
 glVertex2i(x2,y2);
 glVertex2i(x1,y2);
 glEnd();
}

void display(void)
{
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 glColor4f(0.0f,0.0f,1.0f,1.0f);
 SquareFill2D(x,y,x+50,y+50);
 glutSwapBuffers();

}
void idle(void)
{
 if (DIDevice!=NULL){
 // デバイスの直接データを取得する
 HRESULT hr = DIDevice->Poll();
 if (FAILED(hr)){
  hr = DIDevice->Acquire();
  while (hr==DIERR_INPUTLOST)hr = DIDevice->Acquire();
 }
 hr = DIDevice->GetDeviceState(sizeof(DIJOYSTATE2), &JOYPAD);
 if (SUCCEEDED(hr)){
  //方向キー
  if(JOYPAD.lX<-Threshold)x-=2;
  if(JOYPAD.lX>Threshold)x+=2;
  if(JOYPAD.lY<-Threshold)y-=2;
  if(JOYPAD.lY>Threshold)y+=2;

  //ボタン 128個まで 空制御 コピペ用
  if(JOYPAD.rgbButtons[0]);//ボタン1
  if(JOYPAD.rgbButtons[1]);//ボタン2
 }
 }
 Sleep(1);
 glutPostRedisplay();
}

void Init(){
 glClearColor(1.0, 1.0, 1.0, 1.0);
 glOrtho(0, WIDTH, HEIGHT, 0, -1, 1);
 EnumWindows(enumWindowsProc,0);
 Init_DirectInput();
}
int main(int argc, char *argv[])
{
 glutInitWindowPosition(100, 100);
 glutInitWindowSize(WIDTH, HEIGHT);
 glutInit(&argc, argv);
 glutInitDisplayMode(GLUT_RGBA);
 glutCreateWindow("ジョイパッド");
 glutDisplayFunc(display);
 glutIdleFunc(idle);
 Init();
 glutMainLoop();
 return 0;
}

 

最終更新:2014年12月29日 13:17
添付ファイル