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만 알아도 이해하는데 큰 무리는 없습니다.


객체 이동

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

키 코드 명령
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 이벤트가 발생하지만, 첫 눌림과 두 번째 이후의 눌림 사이에 간격이 있기 때문으로 생각할 수 있습니다. 그렇다면, 우리는 첫 눌림에서 지속적으로 연산을 할 수 있도록 구성하면 됩니다.

 

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

 

입력과 연산 분할하기

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

bool keyLayout[256]{};

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

더보기

# 왜 굳이 256인가요?

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

 사실 귀찮아서이고, 메모리 사용량을 줄이려면 더 줄일 수 있습니다. 8배 이상 줄이는 방법이 있기는 하지만 귀찮으니 bool 타입으로 가도록 하죠.[# bool 타입도 내부적으로는 1바이트 메모리를 사용하고 접근할 수 있는 비트가 1입니다.]

 

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

 

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

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

pang2h.tistory.com

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

 

입력

 이제 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차 과정이 끝났습니다.

 

연산

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

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[# 사각형의 중심 위치를 가지고 있는 변수] 변수에 offset 값을 더해줍니다. 이 때, 상하좌우 구분 없이 더해주는 이유는, 이미 offset 값에 부호가 포함되어 있기 때문입니다.

 

그리기

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

 

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

 

# index

728x90
반응형
Comments