후... 드디어 완성했다! 저번 꺼보다 매우 깨끗해보이는 Input Class가 완성되었다.
#pragma once
class Input
{
public:
class Mouse {
friend class Input;
public:
class Event
{
friend class Mouse;
public:
enum State : char
{
Invalid = 0b0000'0000,
KeyDown = 0b0000'0001,
Key = 0b0000'0010,
KeyUp = 0b0000'0100,
};
enum Type : char {
None = -1,
Left = 0,
Middle,
Right,
};
private:
State state;
Type type;
unsigned int pos;
public:
Event(Type type = None, State state = Invalid)
: type(type), state(state)
{}
Event(unsigned int x = 0, unsigned int y = 0)
: type(None), state(Invalid), pos((x << 16) + y)
{}
};
private:
unsigned int x, y;
unsigned char states[3]; //0::Left 1::Middle 2::Right
std::queue<Event> q;
public:
Mouse() = default;
void Reset();
void Read(Event& e);
void On(Event e);
};
class Keyboard
{
friend class Input;
public:
class Event
{
friend class Keyboard;
public:
enum State : char
{
InValid = -1,
KeyDown,
Key,
KeyUp,
Char,
};
private:
State state;
unsigned char code;
public:
Event(unsigned char type, State state = InValid)
: code(type), state(state)
{}
};
private:
static constexpr unsigned int limitBufferSize = 16u;
std::vector<std::bitset<256>> states; // sizeof(std::bitset<256>) == 32 이므로 총 크기는 32 * 3 = 96 바이트로sizeof(bool[256][3]) = 256 * 3 에 비교하여 공간 이득 매우 큼.
std::queue<char> charBuffer; //문자 입력 저장
std::queue<Event> q;
public:
Keyboard() { states.resize(3); }
void Reset();
void Read(Event& e);
void On(Event e);
};
public:
bool GetKey(std::string key);
bool GetKeyDown(std::string key);
bool GetKeyUp(std::string key);
bool GetMouseButton(int num);
bool GetMouseButtonDown(int num);
bool GetMouseButtonUp(int num);
unsigned int GetMouseX();
unsigned int GetMouseY();
void Update();
Input() =default;
LRESULT MessageHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
private:
Mouse mouse;
Keyboard keyboard;
};
비트 연산과 std::bitset을 활용한 결과물이다. 아직 마우스의 휠과 rawInput은 처리하지 않았는데 오늘 하는 김에 완성을 시켜야겠다.
먼저 Wheel의 경우 마우스 휠업과 휠다운이 따로 처리되는 게 아니라 Wheel 메세지가 왔을 때 wheel delta라는 변수값을 받아서 그게 양수인지 음수인지로 휠업과 휠다운을 처리할 수 있다.
case WM_MOUSEWHEEL:
{
mouse.On(Mouse::Event(LOWORD(lParam), HIWORD(lParam), GET_WHEEL_DELTA_WPARAM(wParam)));
return 0;
}
이런 식으로 GET_WHEEL_DELTA_WPARAM 매크로를 이용해 wParam에서 short형 변수를 가지고 올 수 있다.
그러면 이제 Raw Input 처리를 해보자.
RawInput은 특이하게 먼저 RAWINPUTDEVICE라는 구조체를 만들어서 Register를 해주어야 한다.
RAWINPUTDEVICE rid;
rid.usUsagePage = 0x01;
rid.usUsage = 0x02;
rid.dwFlags = 0;
rid.hwndTarget = nullptr;
RegisterRawInputDevices(&rid, 1, sizeof(rid));
이런 식이다. 0x01 0x02 이런건 마우스를 의미하는 거라고 한다. 마우스만 대상으로 rawInput 처리를 할 것이기 때문에 이거 하나만 등록해준다. 그리고 hwndTarget에 해당 Window의 핸들을 넣어야할지 말아야 할지 몰라서 일단 nullptr로 두었다. 넣으면 해당 윈도우 상에서만 raw input을 감지하는 것 같다.
case WM_INPUT:
{
UINT dataSize;
GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, NULL, &dataSize, sizeof(RAWINPUTHEADER));
if (dataSize > 0)
{
rawdata.resize(dataSize);
if (GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, rawdata.data(), &dataSize, sizeof(RAWINPUTHEADER)) == dataSize)
{
RAWINPUT* raw = reinterpret_cast<RAWINPUT*>(rawdata.data());
if (raw->header.dwType == RIM_TYPEMOUSE && (raw->data.mouse.lLastX !=0 || raw->data.mouse.lLastY !=0))
{
mouse.On(Mouse::Event((int)raw->data.mouse.lLastX, (int)raw->data.mouse.lLastY));
}
}
}
return 0;
}
GetRawInputData 함수를 이용하여 처음에 dataSize를 얻어낸 후에 Input에 멤버로 선언한 std::vector<BYTE> rawData;의 크기를 재조정해준다. Input에 멤버로 넣은 이유는 어차피 raw Input은 1초에도 엄청나게 많이 호출될 수 있는데 할 때마다 공간 할당하기엔 좀 성능 상 안좋을 것 같아서이다.
그다음 다시한번 GetRawInputData 함수를 호출하여 rawData중 mouse 데이터만 추출하여 값이 0이 아니면 이벤트를 호출하도록 해두었다.
'진행과정 기록 > GameEngine' 카테고리의 다른 글
20200201 앞으로 할일 (0) | 2020.02.01 |
---|---|
20200131 Texture, SamplerState (0) | 2020.01.31 |
20200129 Input 클래스 수정 (0) | 2020.01.29 |
20200128 Camera, imgui (0) | 2020.01.28 |
20200122 Vertex, InputLayout (0) | 2020.01.22 |