일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- Tips프로그래밍강좌
- 함수
- VS ERROR
- Win32
- 프로그래밍
- c
- CS
- Programming
- 리뷰
- Tips강좌
- 배열
- 문법
- 이지스퍼블리싱
- c++
- 연산자
- 지식나눔강좌
- 알고리즘
- 포인터
- Desktop
- 백준
- Visual Studio
- c#
- Direct2D
- Windows
- Javascript
- 김성엽
- Kotlin
- tipssoft
- 티스토리
- doit코틀린프로그래밍
- Yesterday
- Today
- Total
F.R.I.D.A.Y.
Win32 - 키보드 입력으로 사각형 움직이기 본문

키보드 입력으로 사각형을 움직여봅니다.
# Win32 프로그래밍을 이해하고 있어야합니다.
# 알지 못하는 경우 여기에서부터 시작할 것을 권장합니다.
# 본 문서는 Direct2D 기반으로 구현합니다. Win32만 알아도 이해하는데 큰 무리는 없습니다.
Index
- 객체 이동
- 입력과 연산 분할하기
- 입력
- 연산
- 그리기
객체 이동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^
위 두 작업을 처리하고 나면 아래와 같이 업그레이드 된 모습을 볼 수 있습니다.
어때요, 훨씬 자연스럽고 끊김 없는 이동이 가능해졌죠?
'DEV > C C++' 카테고리의 다른 글
템플릿 클래스를 상속하는 템플릿 클래스 (0) | 2021.05.21 |
---|---|
클래스 메서드를 가리키는 멤버 함수 포인터 (0) | 2021.05.18 |
원의 충돌 판정 (0) | 2021.05.08 |
사각형의 충돌 판정 (0) | 2021.05.07 |
Win32 - 데스크탑 화면에 윈도우 배치하기 (1) | 2021.04.08 |