F.R.I.D.A.Y.

Win32 - 키보드 입력으로 사각형 움직이기 본문

DEV/C C++

Win32 - 키보드 입력으로 사각형 움직이기

F.R.I.D.A.Y. 2021. 5. 8. 06:09
반응형

 키보드 입력으로 사각형을 움직여봅니다.

 

# Win32 프로그래밍을 이해하고 있어야합니다.

# 알지 못하는 경우 여기에서부터 시작할 것을 권장합니다.

# 본 문서는 Direct2D 기반으로 구현합니다. Win32만 알아도 이해하는데 큰 무리는 없습니다.


Index

  • 객체 이동
  • 입력과 연산 분할하기
    • 입력
    • 연산
    • 그리기

Script from F.R.I.D.A.Y


객체 이동copy^

 키보드 이동으로 객체 이동을 한 번 구현해보겠습니다. 일반적으로 키보드 조작으로 객체 이동을 구현한다고 하면 아래와 같이 구현을 할 것입니다.

키 코드 명령
VK_LEFT 객체.x - offset
VK_UP 객체.y - offset
VK_RIGHT 객체.x + offset
VK_DOWN 객체.y + offset
LRESULT MainWindow::OnKeyDown(WPARAM key)
{
    switch (key) {
        case VK_LEFT:
            curPos.x -= 10.f;
            break;
        case VK_UP:

            curPos.y -= 10.f;
            break;
        case VK_RIGHT:

            curPos.x += 10.f;
            break;
        case VK_DOWN:

            curPos.y += 10.f;
            break;
    }
    InvalidateRect(hWnd, nullptr, false);
    return 0;
}

OnKeyDown 이벤트는 WM_KEYDOWN 이벤트에 매핑되어 있습니다.

 

 이렇게 작성을 하면 아래 영상에서처럼 사각형이 움직이는 것을 알 수 있습니다. 사각형은 중앙 점을 기준으로 (40, 40)으로 그렸습니다.

 

 영상을 보면 움직이는 방향대로 잘 움직이는 것을 볼 수 있습니다. 다만 불편한게 몇 개 보입니다. 이 구조는 상하좌우 네 방향으로만 움직일 수 있고, 움직일 때 첫 움직임과 두 번째 움직임 사이에 딜레이가 발생합니다.

 눌려있는 동안은 계속해서 KEYDOWN 이벤트가 발생하지만, 첫 눌림과 두 번째 이후의 눌림 사이에 간격이 있기 때문으로 생각할 수 있습니다. 그렇다면, 우리는 첫 눌림에서 지속적으로 연산을 할 수 있도록 구성하면 됩니다.

 

 입력과 연산, 그리기를 분할할 필요가 있습니다. 이미 입력과 그리기는 분할이 되어 있으니, 입력에 들어있는 연산 부분을 입력에서 분리하는 것만 하면 되겠네요.

 

입력과 연산 분할하기copy^

 일부 RPG 게임에서 보면, 사용하고자 하는 스킬의 단축키를 직접 정할 수 있습니다. 우리는 이런식으로 키보드 입력을 저장하기 위한 배열을 생성합니다.

bool keyLayout[256]{};

 키가 눌렸다와 눌리지 않았다. 두가지 상태만 저장하면 되므로 bool 타입으로 256까지 생성해줍니다.

왜 굳이 256인가요?

 WM_KEYDOWN 이벤트와 WM_KEYUP 이벤트에서 wParam이 가지고 있는 값은 가상 키 코드[1]입니다. 이러한 키 코드는 1바이트 크기의 값에서 결정되는데요, 때문에 1바이트로 나타낼 수 있는 경우의 수인 256가지로 256으로 정했습니다.

 사실 귀찮아서이고, 메모리 사용량을 줄이려면 더 줄일 수 있습니다. 8배 이상 줄이는 방법이 있기는 하지만 귀찮으니 bool 타입으로 가도록 하죠.[2]

 

 또한, 굳이 이런 식의 구현을 한 이유는 다음과 동일합니다.

 

함수 포인터를 배워야 하는 이유 : 코드 간결화

 많은 사람들이 C언어를 배우기 시작하다가 중간에 막히는 부분이 있습니다. 대표적으로 포인터가 있는데요, 이번에 배울 것 또한 포인터입니다. 이번에 배울 포인터는 기존의 포인터와는 약간

pang2h.tistory.com

 편하게 코드를 사용하고 속도 향상을 꾀하기 위함이죠. 분기문이 많아질수록 프로그램의 속도는 떨어지니까요.

 

입력copy^

 이제 WM_KEYDOWN 메시지가 들어왔을 때 그에 맞는 keyLayout 인덱스 값을 1로, WM_KEYUP 메시지가 왔을 때 그에 맞는 keyLayout 인덱스의 값을 0으로 맞춰주면 됩니다.

LRESULT MainWindow::OnKeyDown(WPARAM key){
    keyLayout[key] = 1;
    return 0;
}

LRESULT MainWindow::OnKeyUp(WPARAM key){
    keyLayout[key] = 0;
    return 0;
}

OnKeyDown메서드와 OnKeyUp 메서드는 각각 WM_KEYDOWN, WM_KEYUP 이벤트에 바인딩 되어있다

 

 이 과정으로 입력은 처리를 했습니다. 이제 다른 메시지 작업을 하더라도 키보드 상태는 쉽게 알 수 있겠죠. 입력과 연산의 분리의 1차 과정이 끝났습니다.

 

연산copy^

 연산 작업은 아래와 같이 처리합니다.

void Calculate(){
    D2D1_POINT_2F offset;
    if(keyLayout[VK_LEFT] == keyLayout[VK_RIGHT]) offset.x = 0.f;
    else if(keyLayout[VK_LEFT]) offset.x = -5.f;
    else offset.x = 5.f;
    
    if(keyLayout[VK_UP] == keyLayout[VK_DOWN]) offset.y = 0.f;
    else if(keyLayout[VK_UP]) offset.y = -5.f;
    else offset.y = 5.f;
    
    curPos.x += offset.x;
    curPos.y += offset.y;
}

 x축 이동과 y축 이동을 분리해 처리합니다. 따라서 축 방향으로만 이동하는 것 뿐만 아니라 대각선 방향으로도 이동이 자유로워졌습니다. 마지막에서 curPos[3] 변수에 offset 값을 더해줍니다. 이 때, 상하좌우 구분 없이 더해주는 이유는, 이미 offset 값에 부호가 포함되어 있기 때문입니다.

 

그리기copy^

 위 두 작업을 처리하고 나면 아래와 같이 업그레이드 된 모습을 볼 수 있습니다.

 

 어때요, 훨씬 자연스럽고 끊김 없는 이동이 가능해졌죠?

 

728x90
반응형