본문 바로가기
Win32 API/기초

Win32 API 기초 : Key Manager (1)

글: 시플마 2024. 7. 1.

기존에는 사각형을 움직이기 위해

키 입력을 GetKeyState 함수를 통해 받았습니다.

 

GetKeyState 함수가 호출되는 순간, 왼쪽 키가 입력되어 있다면

사각형이 왼쪽으로 GetKeyState 함수가 호출되는 순간,

오른쪽 키가 입력되어 있다면 사각형이 오른쪽으로 이동했죠.

 

근데 이렇게 한 프레임 속에서 키 입력의 여부를 따지다 보면

문제가 발생할 수 있습니다. 예를 들어 한 프레임 실행이

시작되었을 때는 왼쪽 키 입력이 발생했는데, 프레임 실행이 

끝날 때 키에서 손을 떼며 입력이 사라졌습니다.

 

그러면 최종적으로 키 입력이 없던 것으로 간주하여

오브젝트가 움직이지 않는 상황이 발생할 수도 있는 것이죠.

 

물론 한 프레임이 실행되는 동안의 시간,

즉 Delta Time은 상당히 짧은 시간이므로

보통 키를 누르자마자 떼도 입력으로 인식하긴 하지만

문제가 될 여지가 분명히 존재한다는 것이죠.

 

이를 해결하기 위해서 키 입력이 발생하는 순간

키 입력에 대한 상탯값을 따로 저장하고

프레임 실행이 시작될 때, 이 저장한 값을 불러와서

키 입력에 대한 동작을 실행하는 것입니다.

 

이러면 이미 키 입력에 대한 상탯값이 저장되어 있으므로

한 프레임 속에서 키 입력에 대한 동작을 확실하게 수행할 수 있죠.

 

 

 


 

 

CKeyMgr.h 파일에 

 

위와 같은 KEY라는 이름의

enum class를 만들었습니다.

 

프로그램에서 지원하고

싶은 키를 나열하였죠.

 

 

그리고 KEY_STATE라는 이름의

 

enum class를 만들었습니다.

 

해당 키의 입력 상태를 나타내는 것으로

 

NONE은 아무것도 눌리지 않은 상태,

TAP은 이전에는 눌리지 않았다가 막 눌린 상태,

AWAY는 이전에는 눌렸다가 막 떼진 상태,

HOLD는 이전부터 현재까지 키가 계속 눌리고 있는 상태입니다.

 

 

또한 tKeyInfo라는 구조체를 하나 추가했습니다.

 

해당 구조체로 만든 객체를 통해 키의 상탯값과

이전에 눌렸는지, 눌리지 않았는지에 대한 정보를

담게 될 것입니다.

 

 

61번째 줄에 CKeyMgr 클래스의

vector형 멤버 m_vecKey를 추가했습니다.

 

프로그램에서 지원하는 키들을 m_vecKey라는

vector에 저장하고 각 칸마다 해당 키에 대한

상탯값과 이전에 눌렸는지, 눌리지 않았는지에 대한

정보를 같이 저장하게 되죠.

 

 

66번째 줄에서는 GetKeyState 함수를 호출하여

키를 인자로 주면 해당 키가 현재 어떤 상태인지

반환해 주는 함수입니다.

 

 

 


 

 

 

이제 CKeyMgr 클래스의 멤버 함수들을 정의해 봅시다.

 

초기화 함수인 init 함수입니다.

 

초깃값으로 아무것도 눌리지 않은 상태인 NONE,

그리고 이전에도 눌린 적이 없으므로 false를 줍니다.

 

이렇게 키들에 대한 정보를 멤버 m_vecKey에

push_back 함수를 통해 여러 가지 정보와 함께

각 키를 저장합니다.

 

이러한 반복문을

키의 개수만큼 반복합니다.

 

 


 

 

 

이제 키 입력도 프로그램이 구동하는 동안, 

 

즉 progress 함수가 작동하는 동안에 

계속 키 입력에 대한 정보를 업데이트해야겠죠.

 

그래서 CKeyMgr 클래스에도

멤버 함수 update를 추가합니다.

 

 

모든 키에 대해 업데이트가 발생해야 하므로

for 문은 키의 개수만큼 반복합니다.

 

 

63번째 줄에 키 입력을 받는 GetAsyncKeyState 함수의

인자로 배열 g_KeyArr의 i번째 인덱스를 주고 있습니다.

 

GetAsyncKeyState 함수는 인자로 

정의되어 있는 값들을 넣어 주어야 합니다.

 

아래 사진처럼

 

왼쪽 키 입력을 인식하게 하려면 16진수 25를 넣어야 하죠.

 

이것을 10진수로 표현하면 37을 넣어야 한다는 것인데,

CKeyMgr.h 파일에 직접 정의한 왼쪽 키의 값은 

0이었죠.

 

이 둘이 같은 것이라고 인식하게 하기 위해

 

g_KeyArr이라는 전역 배열을 만들었습니다.

 

프로그램에서 지원하는 키를 나열한

KEY라는 이름의 enum class의 값을

그대로 전역 배열로 만든 것입니다.

(32번째 VK_MENU는 Alt 키를 의미하며,

35번째 VK_RETURN은 Enter 키를 의미합니다.)

 

 

이렇게 한 후 63번째 줄에 키 입력을 받는 

GetAsyncKeyState 함수의 인자로

배열 g_KeyArr의 왼쪽 키를 의미하는

0번째 인덱스를 주었고, 해당 키가

입력되었다면 66번째 줄의 if 문을 체크하겠죠.

 

이때 m_vecKey의 0번째 인덱스도

왼쪽 키를 의미하죠? 이렇게 전역 배열을 통해

GetAsyncKeyState 함수의 인자와 

m_vecKey에 저장된 키가 일치하도록 하였습니다.

 

 

왼쪽 키가 눌린 상태에서 왼쪽 키의 bPrevPush 값,

즉 이전에 눌렸는지에 대한 여부를 체크합니다.

 

이전에 눌린 적이 있다면 왼쪽 키를 쭉 누르고 있는 상태인

KEY_STATE::HOLD 값을 왼쪽 키의 eState에 대입합니다.

 

만약 왼쪽 키가 눌렸는데 이전에는 눌린 적이 없다면

막 처음 누른 상태를 의미하는 KEY_STATE::TAP 값을 

왼쪽 키의 eState에 대입합니다.

 

일단 키가 눌렸다면 다음 프레임에서는

해당 키가 눌린 적이 있다고 봐야 하므로

76번째 줄의 코드를 통해 왼쪽 키의 bPrevPush 값을

true로 합니다.

 

 

79번째 줄의 else 문에 걸리면

키가 눌리지 않았다는 의미겠죠.

 

왼쪽 키가 이전에는 눌렸으나

현재는 눌리지 않은 상태라면 81번째 줄

if 문 안의 코드가 실행됩니다. 

 

눌렀다가 뗀 상태를 의미하는 KEY_STATE::AWAY 값을 

왼쪽 키의 eState에 대입합니다.

 

이전에 눌린 적이 없고, 현재에도 눌리고 있지 않다면

KEY_STATE::NONE 값을 왼쪽 키의 eState에 대입합니다.

 

일단 키가 떼졌다면 다음 프레임에서는

해당 키가 눌린 적이 없다고 봐야 하므로

90번째 줄의 코드를 통해 왼쪽 키의 bPrevPush 값을

false로 합니다.

 

 


 

 

이제 코어 객체의 progress 함수가 실행되면

 

CKeyMgr 클래스를 통해 생성된 키 매니저 객체도

계속 업데이트가 발생할 것입니다.

 

 

68번째 if 문을 보시면

키 매니저 객체의 GetKeyState 함수를 호출하죠.

 

인자로 왼쪽 키를 넣으면 왼쪽 키의 상탯값이 반환됩니다.

 

반환된 왼쪽 키의 상탯값이 TAP과 같다면

사각형을 왼쪽으로 이동시킵니다.

 

이때 쭉 누른다고 해서 계속 움직이지는 않습니다. 

HOLD 상태와 같은 경우를 체크한 게 아니기 때문이죠.

 

 

오른쪽 키 입력이 들어온 경우도 같습니다.

 

 

만약 68, 73번째 if 문의 비교 구문을

TAP이 아닌, 'AWAY와 같을 경우'로 수정한다면

키를 입력하고 뗀 순간에 사각형이 움직이게 되겠죠.

 

 

 


강의 출처 : https://www.youtube.com/watch?v=nUJgEgTB4F4


 

'Win32 API > 기초' 카테고리의 다른 글

Win32 API 기초 : Scene Manager (1)  (0) 2024.07.04
Win32 API 기초 : Key Manager (2)  (2) 2024.07.03
Win32 API 기초 : Double Buffering  (0) 2024.06.30
Win32 API 기초 : Timer (2)  (0) 2024.06.29
Win32 API 기초 : Timer (1)  (0) 2024.06.27