왠지 이부분에 대해 언급한게 없는 것 같아서 뒤늦게 나마 포스팅 합니다. 게임에서 입력장치에 대한 구현이 없으면 안되겠죠. 보통 PC를 생각하면 키보드와 마우스가 있고, 최근에는 PC에서도 조이스틱도( 엑박패드라든가? ) 많이 사용되고 있기 때문에 이쪽에 대해서도 고려를 해야 할 것 입니다.
키보드와 마우스의 경우 윈도우 메시지를 통해서도 이벤트를 얻어 올수 있지만 Direct Input을 통해서 좀더 많은 정보와 정밀도를 얻을수 있습니다. Direct Input을 통해 입력 장치 정보를 얻어오는 방법은 크게 2가지가 있습니다. 하나는 현재 상태의 정보를 얻어오는것과 버퍼에 저장된 상태 정보를 얻어오는 것입니다.
전자의 경우 말그대로 조사를 하는 현 시점의 상태 정보를 구하는 것이고, 후자의 경우 이전 프레임부터 지금 프레임까지 버퍼에 쌓여진 상태 정보를 얻는 것이죠.
일단 Direct Input을 생성 하는 방법입니다.
다음은 키보드 입력 장치를 생성하는 부분입니다.
이제 매 프레임마다 버퍼에서 키 입력 정보를 얻어 옵니다.
버퍼 방식이 아닌 현재 상태를 얻어올때 하는 방식입니다. GetDeviceData 대신 GetDeviceState 함수를 사용해주면 됩니다.
다음은 마우스입니다.
버퍼에서 마우스 상태 얻어오기
GetDeviceState로 현재 마우스 상태 가져오기
키보드와 마우스의 경우 윈도우 메시지를 통해서도 이벤트를 얻어 올수 있지만 Direct Input을 통해서 좀더 많은 정보와 정밀도를 얻을수 있습니다. Direct Input을 통해 입력 장치 정보를 얻어오는 방법은 크게 2가지가 있습니다. 하나는 현재 상태의 정보를 얻어오는것과 버퍼에 저장된 상태 정보를 얻어오는 것입니다.
전자의 경우 말그대로 조사를 하는 현 시점의 상태 정보를 구하는 것이고, 후자의 경우 이전 프레임부터 지금 프레임까지 버퍼에 쌓여진 상태 정보를 얻는 것이죠.
일단 Direct Input을 생성 하는 방법입니다.
HRESULT ev_CInputManagerDX::InitializeImpl( ev_CWindow* pWindow ) { assert( NULL == m_pDirectInput ); assert( NULL == m_pKeyboard ); assert( NULL == m_pMouse ); HRESULT hr( S_OK ); if( FAILED( hr = DirectInput8Create( GetModuleHandle( NULL ), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&m_pDirectInput, NULL ) ) ) { evWriteLogErr( L"ev_CInputManagerDX::InitializeImpl - Failed to Direct Input Device!!"); return hr; } assert( m_pDirectInput ); m_pKeyboard = new ev_CKeyboardDX( pWindow, m_pDirectInput ); if( FAILED( hr = m_pKeyboard->Initialize() ) ) { evWriteLogErr( L"ev_CInputManagerDX::InitializeImpl - Failed to Create evKeyboard!!"); return hr; } m_pMouse = new ev_CMouseDX( pWindow, m_pDirectInput ); if( FAILED( hr = m_pMouse->Initialize() ) ) { evWriteLogErr( L"ev_CInputManagerDX::InitializeImpl - Failed to Create evMouse!!"); return hr; } return hr; }
다음은 키보드 입력 장치를 생성하는 부분입니다.
HRESULT ev_CKeyboardDX::InitializeImpl( void ) { DIPROPDWORD diProperty; diProperty.diph.dwSize = sizeof(DIPROPDWORD); diProperty.diph.dwHeaderSize = sizeof(DIPROPHEADER); diProperty.diph.dwObj = 0; diProperty.diph.dwHow = DIPH_DEVICE; diProperty.dwData = KEYBOARD_DX_BUFFERSIZE; HRESULT hr( S_OK ); if( FAILED( hr = m_pDirectInput->CreateDevice( GUID_SysKeyboard, &m_pDIKeyboardDevice, NULL ) ) ) { evWriteLogErr( L"ev_CKeyboardDX::InitializeImpl - Failed to Create Keyboard Device!!" ); return hr; } assert( m_pDIKeyboardDevice ); if( FAILED( hr = m_pDIKeyboardDevice->SetDataFormat( &c_dfDIKeyboard ) ) ) { evWriteLogErr( L"ev_CKeyboardDX::InitializeImpl - Failed to SetDataFormat!!" ); return hr; } HWND hWnd = m_pWindow->GetHWND(); if( FAILED( hr = m_pDIKeyboardDevice->SetCooperativeLevel( hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE | DISCL_NOWINKEY ) ) ) { evWriteLogErr( L"ev_CKeyboardDX::InitializeImpl - Failed to SetCooperativeLevel!!" ); return hr; } if( FAILED( hr = m_pDIKeyboardDevice->SetProperty( DIPROP_BUFFERSIZE, &diProperty.diph ) ) ) { evWriteLogErr( L"ev_CKeyboardDX::InitializeImpl - Failed to SetProperty!!" ); return hr; } m_pDIKeyboardDevice->Acquire(); return hr; }
이제 매 프레임마다 버퍼에서 키 입력 정보를 얻어 옵니다.
void ev_CKeyboardDX::UpdateKeyboardState( void ) { assert( m_pDIKeyboardDevice ); DWORD dwBuffSize = KEYBOARD_DX_BUFFERSIZE; DWORD dwDIDataSize = sizeof( DIDEVICEOBJECTDATA ); ZeroMemory( &m_diBuff, sizeof( m_diBuff ) ); HRESULT hr; hr = m_pDIKeyboardDevice->GetDeviceData( dwDIDataSize, m_diBuff, &dwBuffSize, 0 ); if( DI_OK != hr ) { hr = m_pDIKeyboardDevice->Acquire(); while( DIERR_INPUTLOST == hr ) hr = m_pDIKeyboardDevice->Acquire(); hr = m_pDIKeyboardDevice->GetDeviceData( dwDIDataSize, m_diBuff, &dwBuffSize, 0 ); if( FAILED( hr ) ) return; } for( UINT i = 0; i < dwBuffSize; ++i ) { bool ret = true; eKeyCode keyCode = (eKeyCode)m_diBuff[ i ].dwOfs; if( m_diBuff[ i ].dwData & 0x80 ) { if( m_pListener ) m_pListener->KeyPressed( ev_CKeyboardEvent( this, keyCode ) ); } else { if( m_pListener ) m_pListener->KeyReleased( ev_CKeyboardEvent( this, keyCode ) ); } } }
버퍼 방식이 아닌 현재 상태를 얻어올때 하는 방식입니다. GetDeviceData 대신 GetDeviceState 함수를 사용해주면 됩니다.
void ev_CKeyboardDX::UpdateKeyboardState( void ) { HRESULT hr; hr = m_pDIKeyboardDevice->GetDeviceState( sizeof( m_btKeys ), m_btKeys ) ); if( DI_OK != hr ) { hr = m_pDIKeyboardDevice->Acquire(); while( DIERR_INPUTLOST == hr ) hr = m_pDIKeyboardDevice->Acquire(); hr = m_pDIKeyboardDevice->GetDeviceState( sizeof( m_btKeys ), m_btKeys ) ); if( FAILED( hr ) ) return; } for ( int i = 0; i < 256; ++i ) { if( m_btKeys[ i ] & 0x80 ) { m_cKeyState[ i ] |= EVKS_DOWNED; m_cKeyState[ i ] &= ~EVKS_IDLE; } else { m_cKeyState[ i ] |= EVKS_IDLE; m_cKeyState[ i ] &= ~EVKS_DOWNED; } if( EVKS_DOWNED == ( m_cKeyState[ i ] & EVKS_DOWNED ) ) { m_cKeyState[ i ] &= ~EVKS_RELEASED; if( EVKS_PUSHFLAG != ( m_cKeyState[ i ] & EVKS_PUSHFLAG ) ) { m_cKeyState[ i ] |= EVKS_PUSHFLAG; m_cKeyState[ i ] |= EVKS_PRESSED; } else { m_cKeyState[ i ] &= ~EVKS_PRESSED; } } else { m_cKeyState[ i ] &= ~EVKS_PRESSED; if( EVKS_PUSHFLAG == ( m_cKeyState[ i ] & EVKS_PUSHFLAG ) ) { m_cKeyState[ i ] &= ~EVKS_PUSHFLAG; m_cKeyState[ i ] |= EVKS_RELEASED; } else { m_cKeyState[ i ] &= ~EVKS_RELEASED; } } } }
다음은 마우스입니다.
HRESULT ev_CMouseDX::InitializeImpl( void ) { DIPROPDWORD diProperty; diProperty.diph.dwSize = sizeof(DIPROPDWORD); diProperty.diph.dwHeaderSize = sizeof(DIPROPHEADER); diProperty.diph.dwObj = 0; diProperty.diph.dwHow = DIPH_DEVICE; diProperty.dwData = MOUSE_DX_BUFFERSIZE; m_dwCoopSetting = DISCL_FOREGROUND | DISCL_EXCLUSIVE; HRESULT hr( S_OK ); if( FAILED( hr = m_pDirectInput->CreateDevice( GUID_SysMouse, &m_pDIMouseDevice, NULL ) ) ) { evWriteLogErr( L"ev_CMouseDX::InitializeImpl - Failed to Create Mouse Device!!" ); return hr; } assert( m_pDIMouseDevice ); if( FAILED( hr = m_pDIMouseDevice->SetDataFormat( &c_dfDIMouse2 ) ) ) { evWriteLogErr( L"ev_CMouseDX::InitializeImpl - Failed to SetDataFormat!!" ); return hr; } HWND hWnd = m_pWindow->GetHWND(); if( FAILED( hr = m_pDIMouseDevice->SetCooperativeLevel( hWnd, m_dwCoopSetting ) ) ) { evWriteLogErr( L"ev_CMouseDX::InitializeImpl - Failed to SetCooperativeLevel!!" ); return hr; } if( FAILED( hr = m_pDIMouseDevice->SetProperty( DIPROP_BUFFERSIZE, &diProperty.diph ) ) ) { evWriteLogErr( L"ev_CMouseDX::InitializeImpl - Failed to SetProperty!!" ); return hr; } m_pDIMouseDevice->Acquire(); return hr; }
버퍼에서 마우스 상태 얻어오기
void ev_CMouseDX::UpdateMouseState( void ) { assert( m_pDIMouseDevice ); DWORD dwBuffSize = MOUSE_DX_BUFFERSIZE; DWORD dwDIDataSize = sizeof( DIDEVICEOBJECTDATA ); ZeroMemory( &m_diBuff, sizeof( m_diBuff ) ); HRESULT hr; hr = m_pDIMouseDevice->GetDeviceData( dwDIDataSize, m_diBuff, &dwBuffSize, 0 ); if( DI_OK != hr ) { hr = m_pDIMouseDevice->Acquire(); while( DIERR_INPUTLOST == hr ) hr = m_pDIMouseDevice->Acquire(); hr = m_pDIMouseDevice->GetDeviceData( dwDIDataSize, m_diBuff, &dwBuffSize, 0 ); if( FAILED( hr ) ) return; } m_stMouseState.ClearFactor(); bool bMouseMoved = false; for( UINT i = 0; i < dwBuffSize; ++i ) { switch( m_diBuff[ i ].dwOfs ) { case DIMOFS_BUTTON0: if( !DoMouseClick( 0, m_diBuff[ i ] ) ) return; break; case DIMOFS_BUTTON1: if( !DoMouseClick( 1, m_diBuff[ i ] ) ) return; break; case DIMOFS_BUTTON2: if( !DoMouseClick( 2, m_diBuff[ i ] ) ) return; break; case DIMOFS_BUTTON3: if( !DoMouseClick( 3, m_diBuff[ i ] ) ) return; break; case DIMOFS_BUTTON4: if( !DoMouseClick( 4, m_diBuff[ i ] ) ) return; break; case DIMOFS_BUTTON5: if( !DoMouseClick( 5, m_diBuff[ i ] ) ) return; break; case DIMOFS_BUTTON6: if( !DoMouseClick( 6, m_diBuff[ i ] ) ) return; break; case DIMOFS_BUTTON7: if( !DoMouseClick( 7, m_diBuff[ i ] ) ) return; break; case DIMOFS_X: m_stMouseState.iMoveFactorX += m_diBuff[ i ].dwData; bMouseMoved = true; break; case DIMOFS_Y: m_stMouseState.iMoveFactorY += m_diBuff[ i ].dwData; bMouseMoved = true; break; case DIMOFS_Z: m_stMouseState.iMoveFactorZ += m_diBuff[ i ].dwData; bMouseMoved = true; break; default: break; } } if( bMouseMoved ) { if( m_dwCoopSetting & DISCL_NONEXCLUSIVE ) { POINT cursorPos; GetCursorPos( &cursorPos ); ScreenToClient( m_pWindow->GetHWND(), &cursorPos ); m_stMouseState.iX = cursorPos.x; m_stMouseState.iY = cursorPos.y; } else { m_stMouseState.iX += m_stMouseState.iMoveFactorX; m_stMouseState.iY += m_stMouseState.iMoveFactorY; } m_stMouseState.iZ += m_stMouseState.iMoveFactorZ; if( m_stMouseState.iX < 0 ) m_stMouseState.iX = 0; else if( m_stMouseState.iX > m_pWindow->GetWndWidth() ) m_stMouseState.iX = m_pWindow->GetWndWidth(); if( m_stMouseState.iY < 0 ) m_stMouseState.iY = 0; else if( m_stMouseState.iY > m_pWindow->GetWndHeight() ) m_stMouseState.iY = m_pWindow->GetWndHeight(); if( m_pListener ) m_pListener->MouseMoved( ev_IMouseEvent( this, m_stMouseState ) ); } }
GetDeviceState로 현재 마우스 상태 가져오기
void ev_CKeyboardDX::UpdateKeyboardState( void ) { ZeroMemory( &m_stDIMouseState, sizeof( m_stDIMouseState ) ); HRESULT hr; hr = m_pDIMouseDevice->GetDeviceState( sizeof( DIMOUSESTATE2 ), &m_stDIMouseState ); if( DI_OK != hr ) { hr = m_pDIMouseDevice->Acquire(); while( DIERR_INPUTLOST == hr ) hr = m_pDIMouseDevice->Acquire(); hr = m_pDIMouseDevice->GetDeviceState( sizeof( DIMOUSESTATE2 ), &m_stDIMouseState ); if( FAILED( hr ) ) return; } m_stMouse.iMoveFactorX = m_stDIMouseState.lX; m_stMouse.iMoveFactorY = m_stDIMouseState.lY; m_stMouse.iMoveFactorZ = m_stDIMouseState.lZ; for ( int i = 0; i < 8; ++i ) { if( m_stDIMouseState.rgbButtons[ i ] & 0x80 ) { m_stMouse.bMouseBtn[ i ] |= EVKS_DOWNED; m_stMouse.bMouseBtn[ i ] &= ~EVKS_IDLE; } else { m_stMouse.bMouseBtn[ i ] |= EVKS_IDLE; m_stMouse.bMouseBtn[ i ] &= ~EVKS_DOWNED; } if( EVKS_DOWNED == ( m_stMouse.bMouseBtn[ i ] & EVKS_DOWNED ) ) { m_stMouse.bMouseBtn[ i ] &= ~EVKS_RELEASED; if( EVKS_PUSHFLAG != ( m_stMouse.bMouseBtn[ i ] & EVKS_PUSHFLAG ) ) { m_stMouse.bMouseBtn[ i ] |= EVKS_PUSHFLAG; m_stMouse.bMouseBtn[ i ] |= EVKS_PRESSED; } else { m_stMouse.bMouseBtn[ i ] &= ~EVKS_PRESSED; } } else { m_stMouse.bMouseBtn[ i ] &= ~EVKS_PRESSED; if( EVKS_PUSHFLAG == ( m_stMouse.bMouseBtn[ i ] & EVKS_PUSHFLAG ) ) { m_stMouse.bMouseBtn[ i ] &= ~EVKS_PUSHFLAG; m_stMouse.bMouseBtn[ i ] |= EVKS_RELEASED; } else { m_stMouse.bMouseBtn[ i ] &= ~EVKS_RELEASED; } } } }
패왕 현아느님 짤로 마무리