Key_Manager

김주현·2021년 7월 26일
0

Win API

목록 보기
1/14

공부한지는 기간이 좀 지났지만 다른 공부를 기록하는 겸해서 오늘부터 공부한것을 정리해서 블로그에 올려볼려고합니다.
현재 WIN API를 공부하고있습니다.

키매니저

KeyManager.h

#pragma once
#define KEY_LBUTTON 0x00000001 
#define KEY_RBUTTON 0x00000002
#define KEY_LEFT	0x00000004
#define KEY_RIGHT	0x00000008
#define KEY_UP		0x00000010
#define KEY_DOWN	0x00000020

class CKey_Manager
{
public:
	static CKey_Manager* Get_Instance()
	{
		if (!m_pInstance)
			m_pInstance = new CKey_Manager;

		return m_pInstance; 
	}
	static void Destroy_Instance()
	{
		if (m_pInstance)
		{
			delete m_pInstance; 
			m_pInstance = nullptr; 
		}
	}
private:
	CKey_Manager();
	~CKey_Manager();
private:
	static CKey_Manager* m_pInstance; 
public:
	void Key_Update();
	bool Key_Up(DWORD dwKey);
	bool Key_Down(DWORD dwKey);
	bool Key_Pressing(DWORD dwKey);

private:
	DWORD m_dwKey;
	DWORD m_dwKeyDown;
	DWORD m_dwKeyUp;
};

KeyManager.cpp

#include "stdafx.h"
#include "Key_Manager.h"

CKey_Manager* CKey_Manager::m_pInstance = nullptr;
CKey_Manager::CKey_Manager()
	:m_dwKey(0)
	, m_dwKeyDown(0)
	, m_dwKeyUp(0)
{
}


CKey_Manager::~CKey_Manager()
{
}

void CKey_Manager::Key_Update()
	{
		m_dwKey = 0;
		// 0000 0000
		if (GetAsyncKeyState(VK_LBUTTON) & 0x8000)
			m_dwKey |= KEY_LBUTTON;
		// 0000 0001 -> dwKey
		// 0000 0010 | ->KEY_RBUTTON 
		// 0000 0011
		if (GetAsyncKeyState(VK_RBUTTON) & 0x8000)
			m_dwKey |= KEY_RBUTTON;
		if (GetAsyncKeyState(VK_LEFT) & 0x8000)
			m_dwKey |= KEY_LEFT;
		if (GetAsyncKeyState(VK_RIGHT) & 0x8000)
			m_dwKey |= KEY_RIGHT;
	}

bool CKey_Manager::Key_Up(DWORD dwKey)
{
	// 여기서 현재 내가 하고싶은거 키 눌렀다 땠을 때 반응하게끔 하려면 
	// 0000 0111
	// 0000 0001 &
	// 현재 키 눌렀을 때 
	if (m_dwKey & dwKey)
	{
		m_dwKeyUp |= dwKey;
		return false;
	}
	// 눌렀다 땠을 때 
	else if (m_dwKeyUp & dwKey)
	{
		// 0000 0111
		// 0000 0001 ^
		// 0000 0110
		m_dwKeyUp ^= dwKey;
		return true;
	}
	// 이 외의 모든 경우는 거짓. 
	return false;
}

bool CKey_Manager::Key_Down(DWORD dwKey)
{
	// 현재 키가 눌려 있으면서 dwKeyDown에 값이 없을 경우 
	if (m_dwKey & dwKey && !(m_dwKeyDown & dwKey))
	{
		m_dwKeyDown |= dwKey;
		return true;
	}
	//현재 키는 눌려있지 않으면서 dwKeyDown에 값이 있을 경우 
	else if (!(m_dwKey & dwKey) && (m_dwKeyDown & dwKey))
	{
		m_dwKeyDown ^= dwKey;
		return false;
	}
	// 이 외의 모든 조건은 암것도 안함.,그래서 거짓  
	return false;
}

bool CKey_Manager::Key_Pressing(DWORD dwKey)
{
	// 이번 프레임에 키가 눌려있나 안눌려있나만 판별하면 됨. 
	if (m_dwKey & dwKey)
		return true;
	return false;
}

기존의 GetAsyncKeyState(VK_RBUTTON) & 0x8000 방식을 따로 클래스로 빼 키매니저를 통해 관리하는 방법입니다. 이경우 한 프레임마다 값을 초기화 하기때문에 키입력을 다양하게 이용할수있습니다(키가 눌러있는지 눌렀다 땐경우 누르고 있는경우를 모두 판별 가능)

우선 키매니저클래스는 싱글톤을 이용해 구현합니다.

class CKey_Manager
{
public:
	static CKey_Manager* Get_Instance()
	{
		if (!m_pInstance)
			m_pInstance = new CKey_Manager;

		return m_pInstance; 
	}
	static void Destroy_Instance()
	{
		if (m_pInstance)
		{
			delete m_pInstance; 
			m_pInstance = nullptr; 
		}
	}
private:
	CKey_Manager();
	~CKey_Manager();
private:
	static CKey_Manager* m_pInstance; 

우선 키매니저는 세개의 멤버변수를 가집니다.

DWORD m_dwKey; -키가 눌렸는지
DWORD m_dwKeyDown; -키가 눌린상태인지
DWORD m_dwKeyUp; -키가 떼진 상태인지

세 멤버변수는 각각의 용도에따라 필요할때 사용되며 m_dwKey는 각 프레임마다 0으로 초기화된후 다시 현재 눌린키를 입력받습니다.

다음 각키를 비트가 겹치지않게 정의해줍니다

#define KEY_LBUTTON 0x00000001 
#define KEY_RBUTTON 0x00000002
#define KEY_LEFT	0x00000004
#define KEY_RIGHT	0x00000008
#define KEY_UP		0x00000010
#define KEY_DOWN	0x00000020

각 값들을 비트로 변환해보면 한비트씩 겹치지않는 값을 가집니다.
ex) 0001,0010,0100,1000
그리고 DWORD의 경우 WIN32기준 4바이트 자료형이기때문에 32비트 즉 32개의 키값을 관리할수있습니다.
하지만 어차피 일반키보드는 입력의 한계가있기때문에 32개로도 충분한 입력을 구현할수있습니다.

Key_Update

다음은 Update부분입니다 update는 로직을 제어하는 부분에서(저의 경우 MainApp.cpp) update를 해주면됩니다

void CKey_Manager::Key_Update()
	{
		m_dwKey = 0;
		// 0000 0000
		if (GetAsyncKeyState(VK_LBUTTON) & 0x8000)
			m_dwKey |= KEY_LBUTTON;
		// 0000 0001 -> dwKey
		// 0000 0010 | ->KEY_RBUTTON 
		// 0000 0011
		if (GetAsyncKeyState(VK_RBUTTON) & 0x8000)
			m_dwKey |= KEY_RBUTTON;
		if (GetAsyncKeyState(VK_LEFT) & 0x8000)
			m_dwKey |= KEY_LEFT;
		if (GetAsyncKeyState(VK_RIGHT) & 0x8000)
			m_dwKey |= KEY_RIGHT;
	}

업데이트는 한프레임마다 호출될것이고 m_dwKey는 프레임마다 0으로 초기화된후 제가 정의한 키값들을 돌며 현재 눌려있는지 비트연산자를 통해 m_dwKey에 기록할것입니다.

Key_Up

Key_up입니다 키가 떼졌을때를 판별하는 함수입니다.

bool CKey_Manager::Key_Up(DWORD dwKey)
{
	// 여기서 현재 내가 하고싶은거 키 눌렀다 땠을 때 반응하게끔 하려면 
	// 0000 0111
	// 0000 0001 &
	// 현재 키 눌렀을 때 
	if (m_dwKey & dwKey)
	{
		m_dwKeyUp |= dwKey;
		return false;
	}
	// 눌렀다 땠을 때 
	else if (m_dwKeyUp & dwKey)
	{
		// 0000 0111
		// 0000 0001 ^
		// 0000 0110
		m_dwKeyUp ^= dwKey;
		return true;
	}
	// 이 외의 모든 경우는 거짓. 
	return false;
}

우선 dwKey로 판별을 원하는 키값을 입력으로 받은후 if (m_dwKey & dwKey)를 통해 현재 눌려있는지 판별합니다 true라면 현재 눌려있다-> 떼어지지않았다 이기 때문에 m_dwKeyUp |= dwKey로 dwKey에 해당하는 비트를 1로 만들어주고 false를 반환합니다. 이를 1번조건이라하겠습니다

추후 프레임이 지나고 키가떼졌다면 dwKey에 해당하는 비트는 0이 될것이고 1번조건문을 지나 else if (m_dwKeyUp & dwKey) 를 통해 눌렸었는지 를 판별합니다.
눌려있었지만 현재 눌려있지않다 -> 지금 떼었다가 되기때문에
이때 단한번 true를 반환해주고 이를위해 m_dwKeyUp ^= dwKey;를통해 dwKey에 해당하는m_dwKeyUp의 비트를 0으로 만들어줍니다

Key_up 위에 말한 로직을 제외하고는 모두 false를 반환합니다.
(ex 한번도 누르지않은경우)

Key_Down

bool CKey_Manager::Key_Down(DWORD dwKey)
{
	// 현재 키가 눌려 있으면서 dwKeyDown에 값이 없을 경우 
	if (m_dwKey & dwKey && !(m_dwKeyDown & dwKey))
	{
		m_dwKeyDown |= dwKey;
		return true;
	}
	//현재 키는 눌려있지 않으면서 dwKeyDown에 값이 있을 경우 
	else if (!(m_dwKey & dwKey) && (m_dwKeyDown & dwKey))
	{
		m_dwKeyDown ^= dwKey;
		return false;
	}
	// 이 외의 모든 조건은 암것도 안함.,그래서 거짓  
	return false;
}

키다운 입니다 키를 처음 눌렀을때 true를 반환합니다

if (m_dwKey & dwKey && !(m_dwKeyDown & dwKey)) 조건문에서

m_dwKeydown의 초기값은 0입니다 그러므로 값을 변경하지않았을때는 false일테고 NOT연산을 통해 이조건문은 참이될것입니다 다음 중괄호안에서 m_dwKeyDown |= dwKey 를통해 키에 해당 비트를 1로 바꿔주고
트루를 반환합니다 그후 에는 이조건을 들어오지못합니다

다음 키를 뗏다면 다시 m_dwKeyDown을 0으로 초기화해주어야합니다.
else if (!(m_dwKey & dwKey) && (m_dwKeyDown & dwKey))
에서 만약 키가 현재 안눌려있고 m_dwKeyDown에 해당 키비트가 1이라면
m_dwKeyDown ^= dwKey;을 통해 다시 0으로 만들어줍니다

이로직은 플레이어가 키를 누름(1을반환후 계속 0반환) ->키를뗌 (계속 0반환) 입니다.

Key_Pressing

bool CKey_Manager::Key_Pressing(DWORD dwKey)
{
	// 이번 프레임에 키가 눌려있나 안눌려있나만 판별하면 됨. 
	if (m_dwKey & dwKey)
		return true;
	return false;
}

현재 키가 눌려있는지 반환하는 함수입니다 업데이트에서 키가 눌렸다면m_dwKey에 해당 키비트 를 켜주기때문에 dwKey와 앤드 연산후 반환해주면됩니다.

줄수를 줄이자면 바로 return (m_dwKey & dwKey) 해줄수있겠죠?

마무리

키매니저를 알아봤습니다 이전에는 플레이어 클래스에서 시간을 멤버변수로 체크하여 키입력을 제어하였는데 이젠 그럴필요없이 키매니저를 이용해 편리하게 키입력을 제어할수있습니다. 편하고 좋네요

0개의 댓글

관련 채용 정보