CAFE

☆ 2D, 기타 강좌

Direct Input을 이용한 키보드 입력

작성자천동이|작성시간04.08.06|조회수2,089 목록 댓글 0
지난 시간에 배운 내용들은 다 머릿속에 새겨 놓으셨는지요.

키보드, 마우스, 조이스틱 전부다 지난시간에 배운 순서와 유사한 순서를 가집니다.

조이스틱의 경우에는 몇가지 순서가 추가 되기는 하지만... 차차 알아 볼 것이구요.


흠... 이번시간에 만들어볼 소스는 DInput을 이용해서 키보드 입력을 받는 소스인데요.

"윈도우 모드에서의 DirectDraw"시간에 배웠던 그 소스와 동일한 내용의 소스입니다.

단지 이것을 DirectInput을 이용해서 키 입력을 받도록 했다는 것만 빼면, 똑같습니다.


자, 그럼 소스를 보도록 하죠.

* Common.h.

#ifndef _COMMON_H_
#define _COMMON_H_

#include

// DX 라이브러리를 프로젝트에 명시적으로 포함시킨다.
#pragma comment(lib, "dxguid.lib") // "dxguid.lib".
#pragma comment(lib, "ddraw.lib")  // "ddraw.lib".
#pragma comment(lib, "dinput.lib") // "dinput.lib".

//////////////
// 상수 정의 //
//////////////

#define ERR             -1  // 에러 처리용 코드 -1.

#define SCREEN_BPP       16     // Bits per pixel.
#define SCREEN_WIDTH    800  // 화면의 가로 크기.
#define SCREEN_HEIGHT  600 // 화면의 세로 크기.
#define CLASS_NAME      "Direct Input Example"

////////////////
// 매크로 함수 //
////////////////
#define KEYUP(KeyCode)         ((GetAsyncKeyState(KeyCode))? 0 : 1)
#define KEYDOWN(KeyCode)    ((GetAsyncKeyState(KeyCode))? 1 : 0)

#define SAFERELEASE(Object) if(Object) { Object->Release(); Object = NULL; }

#endif // end of ifndef _COMMON_H_.

흠.... 당연한 얘기겠지만, DirectInput을 사용하기 위해서는 프로젝트에 "dinput.lib"를 포함 시켜 주셔야합니다.

이런건 깜빡하시면 안되겠죠?^^

* Game.h

#ifndef _GAME_H_
#define _GAME_H_

#define DIRECTINPUT_VERSION 0x0700 // 우리는 DirectInput7버전을 사용할 것이다.

#include "Common.h"
#include "ddraw.h"
#include "dinput.h"

#define RLimit  ClientRect.right-lstrlen(str)*8
#define BLimit  ClientRect.bottom-15
#define Movement 5 
// 이동량.

#define DIKEYDOWN(data, n)  (data[n] & 0x80)

class CGame
{
private:
    HDC hDC;
    HWND hWnd;
   
    bool bPause; // 현재 게임이 정지 상태인지의 여부.
    HINSTANCE hInst;
    LPDIRECTDRAW7 lpdd;

    BYTE KeyState[256];
    LPDIRECTINPUT lpdi;
    LPDIRECTINPUTDEVICE lpdiKbd;

    char str[50];
    POINT pt;            // 클라이언트의 화면 좌표.
    RECT ClientRect; // 클라이언트 렉트.
    COORD Text;      // 문자열의 좌표.
    COORD PreText;
// 문자열의 이전 좌표.

    DDSURFACEDESC2 ddsd;
    LPDIRECTDRAWSURFACE7 lpddSPrimary; // Primary Surface.
public:
    int Init(HWND m_hWnd, HINSTANCE m_hInst); // 게임 초기화.
    int Main();               // 게임 메인.
    int Shutdown();       
// 게임 종료.

    int GetClientPos();   // 클라이언트 영역의 화면 위치를 얻어온다.

    int SetAcquire();
    int GetInput();          // 키 입력을 받는다.
};

#endif // end of ifndef _GAME_H.


흠...

가장 먼저 제일 위에서, 우리는 Direct Input의 7,0 버전을 사용할 것이므로,

DIRECTINPUT_VERSION 를 0x0700으로 재정의(redefinition)해 주었습니다.

디폴트 값은 0x0800으로 8.0 다이렉트 인풋이므로, 재 정의를 하지 않는다면 8.0 인풋을 사용하게 되겠지요.

또, DIKEYDOWN()이라는 새로운 키입력 매크로 함수를 만들었는데요. 이것에 대해서는 지난시간에 설명 드렸죠?

그리고 Direct Input을 사용하기 위해서는 윈도우의 인스턴스 핸들이 필요하기 때문에,

Init() 함수의 매개변수에 hInstance도 받을수 있도록 수정했습니다.


* Game.cpp

#include "Game.h"

int CGame::GetClientPos()
{
    pt.x = 0;
    pt.y = 0;

    ClientToScreen(hWnd, &pt); // 클라이언트 영역의 화면 좌표를 구해온다.
    GetClientRect(hWnd, &ClientRect); // 클라이언트 렉트를 구한다.
   
    return 0;
}

int CGame::Init(HWND m_hWnd, HINSTANCE m_hInst)
{
    hWnd = m_hWnd;
    hInst = m_hInst;

    // DirectDraw 객체 생성.
    if( FAILED(DirectDrawCreateEx(NULL, (LPVOID*)&lpdd, IID_IDirectDraw7, NULL)) )
        return ERR;
    if( FAILED(lpdd->SetCooperativeLevel(hWnd, DDSCL_NORMAL)) )
        return ERR;
   
    // 프라이머리 서페이스 생성.
    memset(&ddsd, 0, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
   
    if( FAILED(lpdd->CreateSurface(&ddsd, &lpddSPrimary, NULL)) )
        return ERR;

    // DirectInput 객체 생성.
    if( FAILED(DirectInputCreate(hInst, DIRECTINPUT_VERSION, &lpdi, NULL)) )
        return ERR;
    if( FAILED(lpdi->CreateDevice(GUID_SysKeyboard, &lpdiKbd, NULL)) )
        return ERR;
    if( FAILED(lpdiKbd->SetDataFormat(&c_dfDIKeyboard)) )
        return ERR;
    if( FAILED(lpdiKbd->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE|DISCL_BACKGROUND)) )
        return ERR;

    bPause = false;
    if( SetAcquire() == ERR)
        return ERR;
    
    lstrcpy(str, "메롱");

    Text.X = 0;
    Text.Y = 0;
    PreText.X = 1;
    PreText.Y = 1;
   
    GetClientPos(); // 클라이언트의 화면상 위치를 구한다.

    return 0;
}

int CGame::SetAcquire()
{
    if( bPause )
    {
        if( FAILED(lpdiKbd->Unacquire()) )
            return ERR;
    }
    else
    {
        if( FAILED(lpdiKbd->Acquire()) )
            return ERR;
    }

    return 0;
}

int CGame::GetInput()
{
    if( FAILED(lpdiKbd->GetDeviceState(sizeof(KeyState), (LPVOID)KeyState)) )
        return ERR;
   
    if( DIKEYDOWN(KeyState, DIK_ESCAPE) )
    {
        bPause = true;
        return ERR;
    }

    // 방향키 입력 처리 멀티키 입력도 가능.
    if( DIKEYDOWN(KeyState, DIK_LEFT) )
        Text.X -= (Text.X < Movement)? 0 : Movement;
    if( DIKEYDOWN(KeyState, DIK_RIGHT) )
        Text.X += (Text.X >= RLimit-Movement)? 0 : Movement;
    if( DIKEYDOWN(KeyState, DIK_UP) )
        Text.Y -= (Text.Y < Movement)? 0 : Movement;
    if( DIKEYDOWN(KeyState, DIK_DOWN) )
        Text.Y += (Text.Y > BLimit-Movement)? 0 : Movement;

    return 0;
}

int CGame::Main()
{
    if( GetInput() == ERR )
        return ERR;

    // 만약 문자열이 이동하지 않았다면,
    if( Text.X == PreText.X && Text.Y == PreText.Y )
        return 0;
// 그려주지 않고 리턴한다.

    lpddSPrimary->GetDC(&hDC);

    // 이전 위치의 문자열을 지워주고,
    SelectObject(hDC, GetStockObject(BLACK_PEN));
    SelectObject(hDC, GetStockObject(BLACK_BRUSH));

    Rectangle(hDC,
              pt.x + PreText.X,
              pt.y + PreText.Y,
              pt.x + PreText.X+lstrlen(str)*8,
              pt.y + PreText.Y+15);
   
    // 문자열을 그려준다.
    SetBkColor(hDC, RGB(0, 0, 0));
    SetTextColor(hDC, RGB(255, 255, 0));
    TextOut(hDC, pt.x+Text.X, pt.y+Text.Y, str, lstrlen(str));

    lpddSPrimary->ReleaseDC(hDC);

    Sleep(50);

    // 문자열의 이전위치 기억.
    PreText.X = Text.X;
    PreText.Y = Text.Y;

    return 0;
}

int CGame::Shutdown()
{
    SetAcquire();

    SAFERELEASE( lpdiKbd );
    SAFERELEASE( lpdi );
    SAFERELEASE( lpddSPrimary );
    SAFERELEASE( lpdd );

    return 0;
}


흠... 앞서 말씀 드린것과 같이 "윈도우 모드" 코딩법을 배울때 만들었던 소스와 거의 흡사합니다.

Init() 함수를 보시면, Direct Input 객체를 생성하는 부분이 있는데,

지난시간에 말씀 드렸던 내용 그대로입니다. 맞죠?^^


그리고 키보드를 Acquire 시키는 부분을 따로 SetAcquire()라는 함수로 빼서 구현을 했는데,

이것은 조금더 프로그램에 유연성을 가하기위해서 한 것이구요.

bPause라는 변수가 true이면, 현재 일시 정지 상태이므로, UnAcquire()를 수행하고, false이면, 게임 중이므로 Acquire()를 수행하도록 하였습니다.


그리고 키 입력 부분은 따로, GetInput() 이라는 함수를 만들어서 Main() 함수 밖으로 키입력을 뺐습니다.

역시 프로그램의 유연성을 위해서 기능별로 함수를 분리한 것이구요.

키 입력은 DIKEYDOWN() 함수를 이용해서, 받을수 있도록 Game.h에서 매크로 함수로 지정을 해 놓았습니다.

KeyState는 BYTE형 변수를 256개 가지는 배열이구요.

또한, ESC 키를 누르면, bPause를 true로 만들어서, 종료시에 UnAcquire()를 하고 나갈수 있도록 처리해 두었습니다.


이럴 수가... 오늘 이 시간에는 별로 설명할 것이 없네요....

키보드 입력 강좌는 그럼 간단히 이걸로 마치기로 하고요.

다음시간에는 마우스 입력을 처리하는 것을 한번 공부해보도록 해요^^;

다음검색
현재 게시글 추가 기능 열기

댓글

댓글 리스트
맨위로

카페 검색

카페 검색어 입력폼