본문 바로가기

프로그래밍/공부관련

Direct X - Direct Input



DX관련 입력중 창이 비활성화될때 입력이 되지않았던 문제를 해결코자 검색중 찾아낸방법....
간단하게 init에서 설정할때 관련인자만 살짝바꿔주면된다. 본 설명에서 검색하면 쉽게 찾아볼수있다.



참고 >
 여기에서 언급하는 Direct X 의 델파이용 헤더는  GMA나 VTOOL에서 구할 수
 있다.

------------------------------------------------------------------------
### 델파이로 하는 Direct X - Direct Input (1/2)
------------------------------------------------------------------------
                                   작성자 : 안영기 ( HiTEL ID : SMgal )



Direct Input 이란 무엇인가 ?  Direct X 의 구성 요소 중에서 Direct Setup
다음으로 쉬운 요소이다.

Direct Input은 입력 장치에 대한 모든 정보를 제어하기 위한 Direct X의 구
성요소인데, 주로 관리하는 쪽이 키보드, 마우스, 조이스틱이다.

마우스로는 3차원 마우스까지 지원하고, 조이스틱으로는 일반적인 2축 4버튼
뿐만 아니라 사이더와인더같은 비행기 조종간이나 스티어링 기능이 있는 레이
스용 조이스틱까지 지원한다. ( 하지만 필자가 3차원 마우스나 조이스틱을 가
지고있지 않기 때문에 강좌에서는 언급하지 않겠다. )

Direct Input 강좌는 총 2 편으로 구성될 예정인데, 1편은 키보드 관리고 2편
은 마우스 관리로 할 예정이다.

------------------------------------------------------------------------

(1) Direct Input 의 Keyboard 를 왜 사용하는가 ?

윈도우즈에는 키보드를 감지하기 위한 좋은 API 함수가 이미 있다.

GetAsyncKeyState() 라는 함수인데  이 함수만 사용해도 현재의 키가 눌러져
있는지..  아니면 이 함수를 호출하기 전에  특정 키가 눌러진 적이 있는지를
알 수 있다.

일반적인 게임( 윈도우 기반의 게임 )에서는 이 함수만으로도 충분히 키보드
관리를 할 수 있고 Direct X 기반의 게임이라고 하더라도 이 함수로도 충분히
키보드 입력에 무리없이 사용할 수 있다.

그렇다면 Direct Input 의 Keyboard 를 왜 사용하는가 ?

필자가 생각하는 이유는 이렇다... Direct Input 의 Keyboard 는 DOS 시절에
게임 프로그래머가 키보드 인터럽트를 가로채서 만든  Multi Key 라는 기법에
가장 가까운 용법으로 쓰이기 때문에 게임 프로그래머들에게 익숙한 사용법을
제공한다. 그리고 키 조합이 많은 아케이드나 액션 게임등에서 키조합이 간단
하고 그 속도가 빠르다.

이 정도면 충분한 이유가 되었다고 생각하고 구현으로 들어가겠다.

------------------------------------------------------------------------

(2) Direct Input 의 Keyboard 객체 구현

| type
|
|    TKeyState = array[0..255] of Byte;
|
|    TDxInput = class
|       constructor Create(hInstance : integer; Handle : integer);
|       procedure   Free;
|    private
|    public
|       DirectInput         : IDirectInput;
|       DirectInputKeyboard : IDirectInputDevice;
|       procedure GetKeyState(var KeyState : TKeyState);
|    end;

이렇게 간단한 객체로 Direct Input 의 Keyboard 객체가 만들어진다.

생성자, 소멸자, 그리고 변수 2 개와 함수하나...

우리는 항상 그렇듯 10줄의 설명 보다는 10줄의 코드를 더 빨리 이해하기 때
문에 implementation에 구현되어 있는 객체 메소드를 직접 보면서 설명하겠다.

아래에 보이는 것이 생성자다.
파라메터는 Instance와 Handle을 받는다.

| constructor TDxInput.Create(hInstance : integer; Handle : integer);
| var
|    HR : HResult;
| begin
|    // 선조의 Create를 실행한다.
|    Inherited Create;
|
|    // Direct Input 객체를 만든다.
|    HR := DirectInputCreate(hInstance,DIRECTINPUT_VERSION,
|                                      DirectInput,nil);
|    if HR <> DI_OK then exit;
|
|    // 키보드 디바이스를 생성한다.
|    HR := DirectInput.CreateDevice(GUID_SysKeyboard,
|                                   DirectInputKeyboard,nil);
|    if HR <> DI_OK then exit;
|
|    // 키보드에 대한 데이터 포맷을 설정한다.
|    HR := DirectInputKeyboard.SetDataFormat(c_dfDIKeyboard);
|    if HR <> DI_OK then exit;
|
|    // 상호 협력 레벨을 설정한다.
|    HR := DirectInputKeyboard.SetCooperativeLevel(Handle,
|                               DISCL_NONEXCLUSIVE or DISCL_FOREGROUND);
|    if HR <> DI_OK then exit;
|
|    // 키보드의 접근 권한을 얻는다.
|    HR := DirectInputKeyboard.Acquire;
|    if (HR <> DI_OK) and (HR <> DI_NOEFFECT) then begin
|       // 실패했을 때 그 원인을 알아 본다.
|       case HR of
|          DIERR_NOTINITIALIZED  :
|             ShowMessage('Direct Input Error : NOTINITIALIZED');
|          DIERR_INVALIDPARAM    :
|             ShowMessage('Direct Input Error : INVALIDPARAM');
|          DIERR_OTHERAPPHASPRIO :
|             ShowMessage('Direct Input Error : OTHERAPPHASPRIO');
|          else
|             ShowMessage('Direct Input Error');
|       end;
|    end;
| end;

이걸로 Direct Input의 Keyboard에 대한 초기화가 모두 끝났다. 그럼 순차적
으로 다시 자세하게 설명해 보면.....

1) DirectInputCreate(hInstance,DIRECTINPUT_VERSION,DirectInput,nil);

  Direct Input 객체를 생성하는 함수다.

  첫번째 파라메터 : 이 객체를 생성하려는 프로그램의 인스턴스
  두번째 파라메터 : Direct Input 의 버전 넘버, 아래처럼 정의되어 있다.
                    {$IFDEF DIRECTX3}
                       DIRECTINPUT_VERSION = $0300;
                    {$ELSE}
                       DIRECTINPUT_VERSION = $0500;
                    {$ENDIF}
  세번째 파라메터 : Direct Input 의 주 객체인 IDirectInput 로 선언된 변
                    수가 var 로 들어가게 된다. 성공하면 nil이 아닌 값이
                    들어온다.
  네번째 파라메터 : 무조건 nil을 쓴다.

2) DirectInput.CreateDevice(GUID_SysKeyboard,DirectInputKeyboard,nil);

  사용할 디바이스를 생성한다.

  첫번째 파라메터 : 사용할 디바이스의 GUID를 넣는다. 이미 정의되어 있는
                    '시스템 키보드'에 대한 GUID인 GUID_SysKeyboard를 넣
                    으면 된다.  참고로 강좌 2편에 나올 '마우스' 에 대한
                    GUID는 GUID_SysMouse 이다.
  두번째 파라메터 : IDirectInputDevice 로 선언된 변수가 var 로 들어간다.
                    역시 성공하면 nil이 아닌 값이 들어 온다.
  세번째 파라메터 : 무조건 nil을 쓴다.

3) DirectInputKeyboard.SetDataFormat(c_dfDIKeyboard);

  디바이스에 대한 데이터 포맷을 설정한다.
  현재는 키보드 디바이스이므로 키보드에 대한 데이터 정보를 보냈다.

  첫번째 파라메터 : 나중에 Direct Input으로부터 돌려 받을 데이터의 형의
                    구조체를 넣는다. 이미  c_dfDIKeyboard, c_dfDIMouse,
                    c_dfDIJoystick, c_dfDIJoystick2 등이  정의되어 있으
                    며 자신만의 데이터 형도 정의할 수 있다.


4) DirectInputKeyboard.SetCooperativeLevel(Handle,
                               DISCL_NONEXCLUSIVE or DISCL_FOREGROUND);

  상호 협력 레벨을 설정한다. 이것은 디바이스를 자신이 독점할 것인가? 아
  니면 다른 프로그램도 동시에 사용할 수 있게 하느냐에 대한 것을 정의 한
  다. 파라메터 이름을 보면 알겠지만 NONEXCLUSIVE 하면서 FOREGROUND 에서
  만 동작하도록 했다. ( 쉽게 말하면 딴 프로그램도 키보드 쓸 수있고 현재
  Handle을 보낸 윈도우에 Focus 가 있을 때만  키보드 정보를 받을 수 있게
  설정했다. )

  첫번째 파라메터 : 키보드 정보를 받을 윈도우의 핸들

  두번째 파라메터 : 협력 레벨에 대한 플래그가 들어간다.

       DISCL_EXCLUSIVE    - 딴 프로그램에서는 Aquire() 하고 있는 동안에
                            키보드를 사용할 수가 없다.
       DISCL_NONEXCLUSIVE - 딴 프로그램과 킵드 정보를 공유한다.
       DISCL_FOREGROUND   - 윈도우에 Focus가 있을 때만 키보드 정보를 받
                            을 수 있다.
       DISCL_BACKGROUND   - 윈도우가 비활성되어도 키보드 정보를 받을 수
                            가 있다.

       ( DISCL_EXCLUSIVE 와 DISCL_NONEXCLUSIVE 는 동시에 사용할 수 없고
         DISCL_FOREGROUND 와 DISCL_BACKGROUND 도 마찬가지다. )

5) DirectInputKeyboard.Acquire;

  디바이스의 접근 권한을 얻는다. 현재는 키보드로 정의되어 있으므로 키보
  드에 대한 접근 권한이다. Acquire를 호출 하지 않으면 키보드의 데이터를
  가져 올 수 없다.

  그리고 리턴 값이 위의 다른 함수와는 좀 다르다는 것을 느꼈을 것이다.

       if (HR <> DI_OK) and (HR <> DI_NOEFFECT) then

  이런 식으로 성공했을 때의 값은 DI_OK 뿐만 아니라 S_FALSE 라는 값일 수
  도 있다. DI_OK 는 Direct Input이 성공했을 때 항상 돌려주는 HResult 값
  이며, DI_NOEFFECT는 이미 Acquire를 이미 호출 한 상태일 때의 리턴 값이
  다.


이렇게 생성자에 대한 부분이 구성된다.


그러면 이번에는 소멸자에 대해서 알아 보자.

| procedure   TDxInput.Free;
| begin
|    // 키보드 접근 권한 해제
|    DirectInputKeyboard.Unacquire;
|
|    // Direct Input 디바이스 객체 해제
|    DirectInputKeyboard._Release;
|    Pointer(DirectInputKeyboard) := nil;
|
|    // Direct Input 객체 해제
|    DirectInput._Release;
|    Pointer(DirectInput) := nil;
|
|    // 선조의 Free를 실행한다.
|    Inherited;
| end;

생성자에 비해서는 비교적 간단하다. 그럼 설명 하자면...

1) DirectInputKeyboard.Unacquire;

  Aquire 메소드에 의해 얻어진 접근 권한을 해제한다.

2) DirectInputKeyboard._Release;
  Pointer(DirectInputKeyboard) := nil;

  Direct Input 디바이스 객체를 해제한다. COM 에서는 그냥 Release를 사용
  하는데 _Release 사용하는 이유는, 델파이 2 의 OLE2 와는 다른, 델파이 3
  의 이상에서 지원하는 IUnknown 인터페이스를 사용하기 때문이다.

3) DirectInput._Release;
  Pointer(DirectInput) := nil;

  Direct Input 객체를 해제한다. 왜 Release라는 과정이 필요한지는 COM 에
  관련된 책자를 읽어 보기 바란다. 참고로 Direct X 는 모두 COM 기반이다.


그럼 이제 딱 하나의 함수만 남았다.

| procedure   TDxInput.GetKeyState(var KeyState : TKeyState);
| var
|    HR : HResult;
| begin
|    // 키보드의 상태를 읽는다.
|    HR := DirectInputKeyboard.GetDeviceState(SizeOf(KeyState),@KeyState);
|    // 만약 입력 장치에 대한 정보를 읽어 버렸으면 다시 Aquire 한다.
|    if HR = DIERR_INPUTLOST then DirectInputKeyboard.Acquire;
| end;

1) DirectInputKeyboard.GetDeviceState(SizeOf(KeyState),@KeyState);

  디바이스의 상태를 읽는다. 현재는 DirectInputKeyboard 가 키보드로 정의
  되었으므로 키보드에 대한 정보를 읽어 온다.

  첫번째 파라메터 : 두번째 파라메터에 들어가는 버퍼의 사이즈를 넣는다.

  두번째 파라메터 : 입력 장치의 정보를 가져오는 버퍼의 포인터를 넣는다.
                    물론 그 구조는 SetDataFormat() 에서 이미 정의했다.

------------------------------------------------------------------------

(3) Direct Input 의 Keyboard 객체 용법

객체를 구현했으니 이제는 사용하는 방법만 남았다.

일단 객체의 선언은 다음처럼 하면 된다.
   DxInput : TDxInput;

그리고 생성은
   DxInput := TDxInput.Create(hInstance,Handle);

소멸은
   DxInput.Free;
   DxInput := nil;

이렇게 사용한다.

그렇다면 용법을 보자...

| var
|    KeyState : TKeyState;
|
| begin
|    // KeyState 에 키보드 상태를 가져온다.
|    DxInput.GetKeyState(KeyState);
|
|    // 오른쪽 화살표키가 눌러져 있는 상태인가 ?
|    if KeyState[DIK_RIGHT] and $80 > 0 then
|    // 왼쪽 화살표키가 눌러져 있는 상태인가 ?
|    if KeyState[DIK_LEFT ] and $80 > 0 then
|    // 위쪽 화살표키가 눌러져 있는 상태인가 ?
|    if KeyState[DIK_UP   ] and $80 > 0 then
|    // 아래쪽 화살표키가 눌러져 있는 상태인가 ?
|    if KeyState[DIK_DOWN ] and $80 > 0 then
| end;

KeyState 에 되돌아 오는 값은 TKeyState 에 선언된 대로 256 개의 byte 배열
이다. 그리고 그 배열들은 Direct Input 헤더에 정의된 DIK_RIGHT 와 같은 상
수에 의해 특정 정보를 가져올 수가 있다.  DOS 때의 스캔코드와 유사하게 키
보드의 실제 물리적인 버튼에 바로 대응한다. 즉 'a' 키를 누르면 무조건 'a'
키로 인식한다. ( 일반적인 키보드라면 Sfhit - 'a' 가 되면 'A' 로 인식하지
만 여기에서는 '왼쪽 Shift' 와 'a'로 따로 인식한다. )

그 배열들에 저장된 값의 MSB(최상위비트)가 1이면 현재 그 키가 눌러진 것이
고 0 이라면 키가 떼어진 것이다. 이 정보를 가지고 DOS 때의 Multi-Key 처럼
핸들링하면 이전에 눌러졌었는지.. 또는 현재 몇개의 키가 조합되었는지 등을
알수가있다. ( BM98 이라는 음악 게임에 보면 동시에 6키를 눌러도 동시에 모
두 인식을 해 내는 것을 볼 수 있을것이다. )

------------------------------------------------------------------------

(4) Direct Input 의 Keyboard 객체 사용 예제

그럼 실제로 사용을 해보자면....

1. 위에서 만든 Direct Input 객체를 따로 Unit 로 저장한다.

2. 새로운 프로젝트를 만들고, Unit1 에는 방금 만든 Direct Input 객체가 있
  는 unit를 uses한다. 그리고 Direct Input 헤더인 DInput.Pas 도 uses한다.

3. 폼에는 타이머, 라벨, 버튼을 만든다.

4. TForm1 의 public 에 'DxInput : TDxInput;' 을 추가한다.

5. 버튼의 클릭 이벤트에 다음과 같이 입력한다.

  DxInput := TDxInput.Create(hInstance,Handle);
  Timer1.Enabled := TRUE;

6. Form 의 Close 이벤트에 다음과 같이 입력한다.

  Timer1.Enabled := FALSE;
  DxInput.Free;
  DxInput := nil;

7. 타이머 이벤트의 Interval을 1로 하고 이벤트에는 다음과 같이 입력한다.

var
  KeyState : TKeyState;
  s        : string;
begin
  DxInput.GetKeyState(KeyState);
  s := '';
  if KeyState[DIK_RIGHT] and $80 > 0 then s := s + 'Right ';
  if KeyState[DIK_LEFT ] and $80 > 0 then s := s + 'Left ';
  if KeyState[DIK_UP   ] and $80 > 0 then s := s + 'Up ';
  if KeyState[DIK_DOWN ] and $80 > 0 then s := s + 'Down ';
  Label1.Caption := s;
end;

8. 실행한다.

9. 버튼을 누르면 Direct Input 의 키보드 핸들링이 시작되는데... 현재는 화
  살표 키에만 반응한다. 동시에 화살표키를 여러개 누르거나  동시에 4개를
  눌러도 모두 반응한다.

나머지 키에 대한 상수 값은 델파이용 Direct X 의 DInput.Pas 에 보면 잘 나
와 있다.


------------------------------------------------------------------------

                                                         1999 / 10 / 31
참고 >
 여기에서 언급하는 Direct X 의 델파이용 헤더는  GMA나 VTOOL에서 구할 수
 있다.

------------------------------------------------------------------------
### 델파이로 하는 Direct X - Direct Input (2/2)
------------------------------------------------------------------------
                                   작성자 : 안영기 ( HiTEL ID : SMgal )



앞 장에서 우리는 Direct Input 에 의한 키보드 상태 읽기를 했었다.

그렇다면 이제는 마우스에 대한 상태를 읽어야 할 차례인데.... 앞 장에서도
간략하게 언급했듯이 키보드에서 했던 방식으로 쉽게 접근 할 수가 있다.

------------------------------------------------------------------------

(1) Direct Input 의 Mouse 를 왜 사용하는가 ?

윈도우즈에는 마우스 위치를 감지하기 위한 좋은 API 함수가 이미 있다.

GetCursorPos() 라는 함수인데  이 함수만 사용해도 현재의 마우스의 위치를
알 수가 있고,  앞에서 언급한 GetAsyncKeyState() 함수에서 VK_LBUTTON 등을
사용하면 현재의 마우스 버튼 상태도 알 수가 있다.

일반적인 게임( 윈도우 기반의 게임 )에서는 이 함수만으로도 충분히 마우스
관리를 할 수 있고 Direct X 기반의 게임이라고 하더라도 이 함수로도 충분히
마우스 상태 정보 얻는데 무리없이 사용할 수 있다.

그렇다면 Direct Input 의 Mouse 를 왜 사용하는가 ?

실제로 마우스 버튼의 상태를 얻기 위해서는 GetAsyncKeyState() 를 사용하
나 Direct Input 를 사용하나 똑같다.  하지만 Direct Input 를 사용하면 동
시에 마우스 버튼들의 상태와 마우스 위치 정보를 얻어 오기 때문에  속도가
빨라진다. 그리고 GetCursorPos() 라는 함수는  단지 윈도우 바탕화면에서의
절대적인 위치만을 돌려주지만 Direct Input 에서는 X, Y 축에 대한 이동 변
량(Delta)을 기준으로 하기 때문에 마우스의 위치를 잡는 것 뿐아니라  마우
스가 단위 시간에 얼마만큼의 속도로 움직였는지까지도 알아 낼 수가 있다.

예를들어  당구 게임을 만드는데  마우스를 밀어서 큐대를 조종한다고 하면
공을 칠 때  순간적인 y 변량으로 힘의 세기를 측정하고 x 의 변량으로 빗나
간 정도를 알 수 있을 것이다.

이 정도면 충분한 이유가 되었다고 생각하고 구현으로 들어가겠다.

------------------------------------------------------------------------

(2) Direct Input 의 Mouse 객체 구현

| type
|    TMouseState = TDIMouseState;
|
|    TDxInput = class
|       constructor Create(hInstance : integer; Handle : integer);
|       procedure   Free;
|    private
|    public
|       DirectInput         : IDirectInput;
|       DirectInputMouse    : IDirectInputDevice;
|       procedure GetMouseState(var MouseState : TMouseState);
|    end;

이렇게 간단한 객체로 Direct Input 의 Mouse 객체가 만들어진다.

생성자, 소멸자, 그리고 변수 2 개와 함수하나...

우리는 항상 그렇듯 10줄의 설명 보다는 10줄의 코드를 더 빨리 이해하기 때
문에 implementation에 구현되어 있는 객체 메소드를 직접 보면서 설명하겠다.

아래에 보이는 것이 생성자다.
파라메터는 Instance와 Handle을 받는다.

| constructor TDxInput.Create(hInstance : integer; Handle : integer);
| var
|    HR : HResult;
| begin
|    // 선조의 Create를 실행한다.
|    Inherited Create;
|
|    // Direct Input 객체를 만든다.
|    HR := DirectInputCreate(hInstance,DIRECTINPUT_VERSION,
|                                      DirectInput,nil);
|    if HR <> DI_OK then exit;
|
|    // 마우스 디바이스를 생성한다.
|    HR := DirectInput.CreateDevice(GUID_SysMouse,DirectInputMouse,nil);
|    if HR <> DI_OK then exit;
|
|    // 마우스에 대한 데이터 포맷을 설정한다.
|    HR := DirectInputMouse.SetDataFormat(c_dfDIMouse);
|    if HR <> DI_OK then exit;
|
|    // 상호 협력 레벨을 설정한다.
|    HR := DirectInputMouse.SetCooperativeLevel(Handle,
|                                  DISCL_EXCLUSIVE or DISCL_FOREGROUND);
|    if HR <> DI_OK then exit;
|
|    // 마우스의 접근 권한을 얻는다.
|    HR := DirectInputMouse.Acquire;
|    if (HR <> DI_OK) and (HR <> DI_NOEFFECT) then begin
|       // 실패했을 때 그 원인을 알아 본다.
|       case HR of
|          DIERR_NOTINITIALIZED  :
|             ShowMessage('Direct Input Error : NOTINITIALIZED');
|          DIERR_INVALIDPARAM    :
|             ShowMessage('Direct Input Error : INVALIDPARAM');
|          DIERR_OTHERAPPHASPRIO :
|             ShowMessage('Direct Input Error : OTHERAPPHASPRIO');
|          else
|             ShowMessage('Direct Input Error');
|       end;
|    end;


이걸로 Direct Input의 Mouse에 대한 초기화가 모두 끝났다.  그럼 순차적으
로 다시 대충 설명해 보면..... ( 1 편에서 자세하게 했기 때문에.. )

1) DirectInputCreate(hInstance,DIRECTINPUT_VERSION,DirectInput,nil);

  Direct Input 객체를 생성하는 함수다.
  Keyboard 일 때와 같은 명령어다.

2) DirectInput.CreateDevice(GUID_SysMouse,DirectInputMouse,nil);

  사용할 디바이스를 생성한다.
  GUID_SysMouse가 첫번째 파라메터로 들어 갔기 때문에 IDirectInputDevice
  로 선언된 변수인 DirectInputMouse은 마우스 디바이스로 생성된다.

3) DirectInputMouse.SetDataFormat(c_dfDIMouse);

  디바이스에 대한 데이터 포맷을 설정한다.
  현재는 마우스 디바이스이므로, 마우스 정보를 위한 구조체 포맷이라는 것
  을 디바이스에게 알려준다.

4) DirectInputMouse.SetCooperativeLevel(Handle,
                               DISCL_EXCLUSIVE or DISCL_FOREGROUND);

  상호 협력 레벨을 설정한다. 키보드 디바이스일 때와는 달리 EXCLUSIVE 로
  설정을 한 것을 눈여겨 보기 바란다.  DISCL_EXCLUSIVE 로 설정을 하면 다
  른 프로그램에 대해서는 배타적으로 마우스 정보를 독점할 수가 있으며 마
  우스 커서는 GDI 환경에서 사라지게 된다.  즉 마우스 커서는 자신이 직접
  그려주어야 하는데... 모든 게임이 그렇듯 디폴트 마우스는 사라지고 자신
  의 게임에 맞는 마우스 커서가 떠야 할 것이다.

  물론 DISCL_NONEXCLUSIVE 로도 설정해도 된다. 그렇게 하면 마우스 커서가
  눈에 보이게 된다. 하지만 마우스를 잡고 난동을 부리면서 아래 위로 커서
  를 움직여 보면 원래 원했던 좌표로부터 자꾸 벗어난다(오차가 난다)는 것
  을 알 수 있을 것이다. ( 여기서 좌표란 논리적으로 변량을 계산해서 나타
  내는 마우스 위치를 말한다. )

  그리고 키보드 할 때 빼 먹은 부분이 있는데, 키보드는 기본적으로 배타적
  으로  사용되어 질수가 없다.  하지만 Direct X 7.0 에서는 키보드도 배타
  적으로 사용될 수 있다고 하니 참고로 알아 두자.

5) DirectInputMouse.Acquire;

  디바이스의 접근 권한을 얻는다. 현재는 마우스로 정의되어 있으므로 마우
  스에 대한 접근 권한이다. Acquire를 호출 하지 않으면 마우스의 데이터를
  가져 올 수 없다. 나머지는 키보드일 때와 같다.


그러면 이번에는 소멸자에 대해서 알아 보자.

| procedure   TDxInput.Free;
| begin
|    // 마우스 접근 권한 해제
|    DirectInputMouse.Unacquire;
|
|    // Direct Input 디바이스 객체 해제
|    DirectInputMouse._Release;
|    Pointer(DirectInputMouse) := nil;
|
|    // Direct Input 객체 해제
|    DirectInput._Release;
|    Pointer(DirectInput) := nil;
|
|    // 선조의 Free를 실행한다.
|    Inherited;
| end;

내용은 키보드 일때와 완전히 같다.


그럼 이제 딱 하나 남은 가장 중요한 함수를 보자.
역시 키보드일 때와 용법은 같다.

| procedure TDxInput.GetMouseState(var MouseState : TMouseState);
| var
|    HR : HResult;
| begin
|    HR := DirectInputmouse.GetDeviceState(SizeOf(MouseState),@MouseState);
|    if HR = DIERR_INPUTLOST then DirectInputMouse.Acquire;
| end;

1) DirectInputMouse.GetDeviceState(SizeOf(KeyState),@KeyState);

  디바이스의 상태를 읽는다. 현재는 DirectInputMouse가 마우스로 정의되었
  으므로 마우스에 대한 정보를 읽어 온다.

  키보드일 때와 다른 점은 TMouseState 라는 구조체이다..

| type
|    TMouseState = TDIMouseState;

  위의 소스에 보면 이런 부분이 있다. DInput 헤더에 있는 TDIMouseState를
  그대로 사용한 것인데 TDIMouseState 의 구조는 다음과 같다.

  TDIMouseState = packed record
      lX: Longint;
      lY: Longint;
      lZ: Longint;
      rgbButtons: Array [0..3] of BYTE;
  end;

  IX - 마우스가 X 축으로 이동한 변량
  IY - 마우스가 Y 축으로 이동한 변량
  IZ - 마우스가 Z 축으로 이동한 변량 ( 3 차원 마우스 지원용이다. )
  rgbButtons[0] - 왼쪽 마우스 버튼이 눌러지면 MSB가 1 그렇지 않으면 0
  rgbButtons[1] - 오른쪽 마우스 버튼이 눌러지면 MSB가 1 그렇지 않으면 0
  rgbButtons[2] - 2 버튼 이상의 마우스 지원용
  rgbButtons[3] - 2 버튼 이상의 마우스 지원용

  이렇게 이루어져 있다.


------------------------------------------------------------------------

(3) Direct Input 의 Mouse 객체 용법

객체를 구현했으니 이제는 사용하는 방법만 남았다.

일단 객체의 선언은 다음처럼 하면 된다.
   DxInput : TDxInput;

그리고 생성은
   DxInput := TDxInput.Create(hInstance,Handle);

소멸은
   DxInput.Free;
   DxInput := nil;

이렇게 사용한다.

그렇다면 용법을 보자...

| var
|    MouseState : TMouseState;
|
| begin
|    // MouseState 에 마우스 상태를 가져온다.
|    DxInput.GetMouseState(MouseState);
|
|    // 현재의 x, y 변량을 더한다.
|    Inc(MousePos.X,MouseState.lX);
|    Inc(MousePos.Y,MouseState.lY);
|    // 마우스 버튼이 눌려졌는지의 여부를 알아 본다.
|    if MouseState.rgbButtons[0] and $80 > 0 then
|    if MouseState.rgbButtons[1] and $80 > 0 then

보다시피 한 번에 마우스 위치 정보와  마우스 버튼 정보를 얻어 오기 때문에
빠를수 밖에 없다. 그리고 윈도우의 메세지등을 거치지 않기고 바로 하드웨어
인터럽트에서 얻어 오기 때문에 부하도 적다.

------------------------------------------------------------------------

(4) Direct Input 의 Mouse 객체 사용 예제

그럼 실제로 사용을 해보자면....

1. 위에서 만든 Direct Input 객체를 따로 Unit 로 저장한다.

2. 새로운 프로젝트를 만들고, Unit1 에는 방금 만든 Direct Input 객체가 있
  는 unit를 uses한다. 그리고 Direct Input 헤더인 DInput.Pas 도 uses한다.

3. 폼에는 타이머, 라벨, 버튼을 만든다.

4. TForm1 의 public 에 'DxInput : TDxInput;' 을 추가한다.
  그리고 'MousePos : TPoint;' 도 추가한다.  이것은 마우스 커서의 현위치
  를 기억해 두기 위해서 필요하다.

5. 버튼의 클릭 이벤트에 다음과 같이 입력한다.

  DxInput  := TDxInput.Create(hInstance,Handle);
  MousePos := Point(0,0);
  Timer1.Enabled := TRUE;

6. Form 의 Close 이벤트에 다음과 같이 입력한다.

  Timer1.Enabled := FALSE;
  DxInput.Free;
  DxInput := nil;

7. 타이머 이벤트의 Interval을 1로 하고 이벤트에는 다음과 같이 입력한다.

var
  MouseState : TMouseState;
  s          : string;
begin
  DxInput.GetMouseState(MouseState);
  Inc(MousePos.X,MouseState.lX);
  Inc(MousePos.Y,MouseState.lY);
  s := Format('(%4d,%4d) ',[MousePos.X,MousePos.Y]);
  if MouseState.rgbButtons[0] and $80 > 0 then s := s + 'Left Button ';
  if MouseState.rgbButtons[1] and $80 > 0 then s := s + 'Right Button ';
  Label1.Caption := s;
end;

8. 실행한다.

9. 버튼을 누르면 Direct Input 의 마우스 핸들링이 시작되는데...  마우스의
  움직임이나 버튼의 누름을 표시해 준다.  동시에 버튼을 눌러도 역시 결과
  는 같다. ( 마우스 좌표의 기준은 Direct Input이 시작할때의 마우스 위치
  가 (0,0) 으로 설정된다. )


------------------------------------------------------------------------

<< 에필로그 >>

지금까지 설명한 것은 Direct X 의 입력 디바이스 부분인 Direct Input 부분
이었습니다.  델파이로 게임 만드시려는 분들께  조금이나마 도움이 되었으면
좋겠습니다.

지금까지 델파이로 Direct Draw, 멀티미디어 타이머, DIB, Direct Input  에
대한 강좌를 했는데..... 앞으로 델파이로 하는 Direct Sound, Direct Music,
Direct Play 에 대한 강좌를 더 하고 싶습니다..

일요일 하루를 투자해서 2편 분량의 강좌를 했으니, 앞으로도 일요일마다 이
정도 분량의 강좌를 해 나가고 싶습니다. 그럼 관심 있으신 분들은 많이 기대
해 주세요..

그럼 마지막으로 한마디 남기겠습니다.

         " A mountain is a mountain, Water is water. "
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                                         1999 / 10 / 31