본문 바로가기

WinAPI

[WinAPI] 게임 프레임워크 - 4. Input

WinAPI 프레임워크에서 키보드, 마우스 입력을 관리하는 Input 클래스를 만들었다. 

 

typedef struct _tagKeyInfo
{
	string strName;
	vector<DWORD> vecKey;
	bool bPress;
	bool bDown;
	bool bUp;

	_tagKeyInfo() :
		bDown(false),
		bPress(false),
		bUp(false)
	{

	}

}KEYINFO, * PKEYINFO;

 

KEYINFO 구조체는 Input 클래스 내에 있다. vecKey는 입력해야 할 키 값'들'이다. Ctrl + S 처럼 두개 이상의 키를 동시에 입력받아야 할 경우가 있기 때문이다. strName은 vecKey의 KEYINFO의 이름이다. bPress, bDown, bUp은 각각 처음 눌렀을 때 부터 뗄 때 까지, 처음 눌렀을 때, 눌렀다가 뗐을 때 true가 된다. 

 

	template<typename T, typename... Types>
	bool AddKey(const T& data, const Types&... arg)
	{
		if (!m_pCreateKey)
			m_pCreateKey = new KEYINFO;

		const char* pTType = typeid(T).name();

		if (strcmp(pTType, "char") == 0 ||
			strcmp(pTType, "int") == 0)
		{
			m_pCreateKey->vecKey.push_back((DWORD)data);
		}
		else
		{
			m_pCreateKey->strName = data;
			m_mapKey.insert(make_pair(m_pCreateKey->strName, m_pCreateKey));
		}
		AddKey(arg...);

		if (m_pCreateKey)
			m_pCreateKey = NULL;

		return true;
	}

 

AddKey 함수 템플릿은 KEYINFO의 정보를 입력하는 가변인자 템플릿이다. 우선 m_pCreateKey라는 KEYINFO가 NULL일 시 생성한다. 그리고 인수를 하나씩 받아 type이 char이거나 int일 시, 입력해야 할 키이므로 vecKey에 저장한다. 아니라면 KEYINFO의 이름이므로 strName에 저장하고 strName이 key이고 m_pCreateKey가 value인 map에 저장한다. AddKey는 Input::Init에서 최초 한번 실행한다.

 

	unordered_map<string, PKEYINFO>::iterator iter;
	unordered_map<string, PKEYINFO>::iterator iterEnd = m_mapKey.end();

	for (iter = m_mapKey.begin(); iter != iterEnd; ++iter)
	{
		int iPushCount = 0;
		for (size_t i = 0; i < iter->second->vecKey.size(); ++i)
		{
			if (GetAsyncKeyState(iter->second->vecKey[i]) & 0x8000)
				++iPushCount;
		}
		if (iPushCount == iter->second->vecKey.size())
		{
			if (!iter->second->bDown && !iter->second->bPress)
			{
				iter->second->bPress = true;
				iter->second->bDown = true;
			}

			else if (iter->second->bDown)							
				iter->second->bDown = false;
			
		}
		else
		{
			if (iter->second->bDown || iter->second->bPress)
			{
				iter->second->bUp = true;
				iter->second->bDown = false;
				iter->second->bPress = false;
			}
			else if (iter->second->bUp)
				iter->second->bUp = false;
		}
	}

 

Input::Update에서는 Init에서 설정한 것을 바탕으로 입력 감지를 한다. m_mapKey를 돌면서 value인 PKEYINFO의 vecKey가 입력될 때마다 iPushCount가 증가한다. vecKey.size와 iPushCount가 같으면 입력할 키들을 모두 입력한 것이므로 눌렀다고 처리한다. 처음에는 bPress와 bDown가 모두 true가 되고 그 다음 Update에서는 bDown이 false가 된다.

그리고 iPushCount가 vecKey.size가 같지 않으면 키를 뗀 상태이므로 bDown,bPress는 false, bUp은 true가 된다.

 

	AddKey('W', "MoveFront");
	AddKey('S', "MoveBack");
	AddKey('A', "MoveLeft");
	AddKey('D', "MoveRight");
	AddKey("Fire", VK_SPACE);
	AddKey(VK_CONTROL, "Skill1", '1');
	AddKey(VK_LBUTTON, "MouseLButton");
	AddKey(VK_RBUTTON, "MouseRButton");


Input::Init에서 AddKey로 키 입력을 지정하는 예시이다. W,S,A,D와 스페이스, 컨트롤, 마우스 좌클릭, 우클릭 입력은 각각의 이름을 가진다.

 

	if (KEYPRESS("MoveLeft")) {
		MoveXFromSpeed(fDeltaTime, MD_BACK);
		m_pAnimation->ChangeClip("RunLeft");
		m_iDir = -1;
	}
	if (KEYPRESS("MoveRight")) {
		MoveXFromSpeed(fDeltaTime, MD_FRONT);
		m_iDir = 1;
	}
	if (KEYDOWN("Fire")) {
		Fire();
        }

 

Player::Input에서 지정한 키 입력을 바탕으로 함수를 실행한다. KEYPRESS는 해당 strName의 KEYINFO가 bPress=true 인지, 즉 키를 누르고 유지중인 상태인지 확인하는 매크로이다. 그래서 A키, D키를 꾹 누르고 있으면 캐릭터가 부드럽게 움직인다. 그리고 KEYDOWN은 키를 최초 누른 순간만 실행되기 때문에 스페이스바를 꾹 눌러도 Fire이 한번만 실행된다.