일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- albert
- SKT
- 패스트캠퍼스후기
- 직장인인강
- 한번에 끝내는 Java/Spring 웹 개발 마스터 초격차 패키지
- 직장인자기계발
- 패스트캠퍼스
- 한번에 끝내는 Java/Spring 웹 개발 마스터 초격차 패키지 Online
- 알버트
- 한번에 끝내는 Java/Spring 웹 개발 마스터 초격차 패키지 Online.
- 패캠챌린지
- AI
- 한번에끝내는Java/Spring웹개발마스터초격차패키지
- R
- Today
- Total
제주 탈출 일지
[3]Keys Down - 2D game programming 본문
이전 character input에서는 단순히 키의 입력만을 받아서 그 입력을 창에 띄워주는 것으로 끝이 났다.
Key Down에서는 현재 키가 눌리고 있는지 아닌지를 확인하는데 중점을 둔다. 그리고 각각의 키는 다른 위치에서 키다운을 표시한다.
전체 소스코드이다.
// Programming 2D Games
// Copyright (c) 2011 by:
// Charles Kelly
// Chapter 2 Keyboard State with Windows API v1.0
// winmain.cpp
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// Function prototypes
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
bool CreateMainWindow(HINSTANCE, int);
LRESULT WINAPI WinProc(HWND, UINT, WPARAM, LPARAM);
// Global variables
HINSTANCE hinst;
HDC hdc; // handle to device context
TCHAR ch = ' '; // character entered
RECT rect; // rectangle
PAINTSTRUCT ps; // used in WM_PAINT
bool vkKeys[256]; // state of virtual keys, false or true
// Constants
const char CLASS_NAME[] = "Keyboard";
const char APP_TITLE[] = "Keys Down";
const int WINDOW_WIDTH = 400; // width of window
const int WINDOW_HEIGHT = 400; // height of window
//=============================================================================
// Starting point for a Windows application
//=============================================================================
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
// Create the main window
if (!CreateMainWindow(hInstance, nCmdShow))
return false;
for (int i=0; i<256; i++) // initialize virtual key array
vkKeys[i] = false;
// main message loop
int done = 0;
while (!done)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
//look for quit message
if (msg.message == WM_QUIT)
done = 1;
//decode and pass messages on to WinProc
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
//=============================================================================
// window event callback function
//=============================================================================
LRESULT WINAPI WinProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
short nVirtKey; // virtual-key code
const short SHIFTED = (short)0x8000;
TEXTMETRIC tm; // structure for text metrics
DWORD chWidth = 20; // width of characters
DWORD chHeight = 20; // height of characters
switch( msg )
{
case WM_CREATE:
// get the text metrics
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
chWidth = tm.tmAveCharWidth; // average character width
chHeight = tm.tmHeight; // character height
return 0;
case WM_DESTROY:
//tell Windows to kill this program
PostQuitMessage(0);
return 0;
case WM_KEYDOWN: // key down
vkKeys[wParam] = true;
switch(wParam)
{
case VK_SHIFT: // shift key
nVirtKey = GetKeyState(VK_LSHIFT); // get state of left shift
if (nVirtKey & SHIFTED) // if left shift
vkKeys[VK_LSHIFT] = true;
nVirtKey = GetKeyState(VK_RSHIFT); // get state of right shift
if (nVirtKey & SHIFTED) // if right shift
vkKeys[VK_RSHIFT] = true;
break;
case VK_CONTROL: // control key
nVirtKey = GetKeyState(VK_LCONTROL);
if (nVirtKey & SHIFTED) // if left control
vkKeys[VK_LCONTROL] = true;
nVirtKey = GetKeyState(VK_RCONTROL);
if (nVirtKey & SHIFTED) // if right control
vkKeys[VK_RCONTROL] = true;
break;
}
InvalidateRect(hwnd, NULL, TRUE); // force WM_PAINT
return 0;
break;
case WM_KEYUP: // key up
vkKeys[wParam] = false;
switch(wParam)
{
case VK_SHIFT: // shift key
nVirtKey = GetKeyState(VK_LSHIFT);
if ((nVirtKey & SHIFTED) == 0) // if left shift
vkKeys[VK_LSHIFT] = false;
nVirtKey = GetKeyState(VK_RSHIFT);
if ((nVirtKey & SHIFTED) == 0) // if right shift
vkKeys[VK_RSHIFT] = false;
break;
case VK_CONTROL: // control key
nVirtKey = GetKeyState(VK_LCONTROL);
if ((nVirtKey & SHIFTED) == 0) // if left control
vkKeys[VK_LCONTROL] = false;
nVirtKey = GetKeyState(VK_RCONTROL);
if ((nVirtKey & SHIFTED) == 0) // if right control
vkKeys[VK_RCONTROL] = false;
break;
}
InvalidateRect(hwnd, NULL, TRUE); // force WM_PAINT
return 0;
break;
case WM_CHAR: // a character was entered by the keyboard
switch (wParam) // the character is in wParam
{
case 0x08: // backspace
case 0x09: // tab
case 0x0A: // linefeed
case 0x0D: // carriage return
case 0x1B: // escape
return 0; // non displayable character
default: // displayable character
ch = (TCHAR) wParam; // get the character
InvalidateRect(hwnd, NULL, TRUE); // force WM_PAINT
return 0;
}
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); // get handle to device context
TextOut(hdc, 0, 0, &ch, 1); // display the character
// Display the state of vkKeys array
// Display 'T' if key is down and 'F' is key is up
for (int r=0; r<16; r++)
{
for (int c=0; c<16; c++)
{
if (vkKeys[r*16+c])
{
SetBkMode(hdc, OPAQUE); // opaque text background
TextOut(hdc,c*chWidth+chWidth*2,r*chHeight+chHeight*2,"T ", 2);
} else {
SetBkMode(hdc, TRANSPARENT); // transparent text background
TextOut(hdc,c*chWidth+chWidth*2,r*chHeight+chHeight*2,"F ", 2);
}
}
}
EndPaint(hwnd, &ps);
return 0;
default:
return DefWindowProc( hwnd, msg, wParam, lParam );
}
}
//=============================================================================
// Create the window
// Returns: false on error
//=============================================================================
bool CreateMainWindow(HINSTANCE hInstance, int nCmdShow)
{
WNDCLASSEX wcx;
HWND hwnd;
// Fill in the window class structure with parameters
// that describe the main window.
wcx.cbSize = sizeof(wcx); // size of structure
wcx.style = CS_HREDRAW | CS_VREDRAW; // redraw if size changes
wcx.lpfnWndProc = WinProc; // points to window procedure
wcx.cbClsExtra = 0; // no extra class memory
wcx.cbWndExtra = 0; // no extra window memory
wcx.hInstance = hInstance; // handle to instance
wcx.hIcon = NULL;
wcx.hCursor = LoadCursor(NULL,IDC_ARROW); // predefined arrow
wcx.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); // gray background brush
wcx.lpszMenuName = NULL; // name of menu resource
wcx.lpszClassName = CLASS_NAME; // name of window class
wcx.hIconSm = NULL; // small class icon
// Register the window class.
// RegisterClassEx returns 0 on error.
if (RegisterClassEx(&wcx) == 0) // if error
return false;
// Create window
hwnd = CreateWindow(
CLASS_NAME, // name of the window class
APP_TITLE, // title bar text
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // default horizontal position of window
CW_USEDEFAULT, // default vertical position of window
WINDOW_WIDTH, // width of window
WINDOW_HEIGHT, // height of the window
(HWND) NULL, // no parent window
(HMENU) NULL, // no menu
hInstance, // handle to application instance
(LPVOID) NULL); // no window parameters
// if there was an error creating the window
if (!hwnd)
return false;
// Show the window
ShowWindow(hwnd, nCmdShow);
// Send a WM_PAINT message to the window procedure
UpdateWindow(hwnd);
return true;
}
점점 소스코드가 길어지지만, 구조는 동일하다. WinMain, CreateMainWindow, WinProc 3개의 함수로 구성되어 있다.
먼저 선언부이다.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// Function prototypes
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
bool CreateMainWindow(HINSTANCE, int);
LRESULT WINAPI WinProc(HWND, UINT, WPARAM, LPARAM);
// Global variables
HINSTANCE hinst;
HDC hdc; // handle to device context
TCHAR ch = ' '; // character entered
RECT rect; // rectangle
PAINTSTRUCT ps; // used in WM_PAINT
bool vkKeys[256]; // state of virtual keys, false or true
// Constants
const char CLASS_NAME[] = "Keyboard";
const char APP_TITLE[] = "Keys Down";
const int WINDOW_WIDTH = 400; // width of window
const int WINDOW_HEIGHT = 400; // height of window
이전 Character input 예제와 다른것은 bool vkKeys[256];이다 이 vkKey에서 키가 눌려있는지 아닌지에 대한 상태를 표시한다. 이외의 다른 내용은 이전 예제와 동일하다.
그 다음 WinMain이다.
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
// Create the main window
if (!CreateMainWindow(hInstance, nCmdShow))
return false;
for (int i=0; i<256; i++) // initialize virtual key array
vkKeys[i] = false;
// main message loop
int done = 0;
while (!done)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
//look for quit message
if (msg.message == WM_QUIT)
done = 1;
//decode and pass messages on to WinProc
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
if문과 while문 사이의 for문이 새롭게 추가되었다. 아까 선언부에서의 vkKeys를 false로 초기화 해주는 작업을 해주고 있다. 그 이외에는 동일하다
다음 이벤트를 담당하는 WinProc함수이다. switch문의 case가 길어진 것을 확인할 수 있다.
switch문의 case는 WM_CREATE, WM_DESTROY, WM_KEYDOWN, WM_KEYUP, WM_CHAR, WM_PAINT가 있다.
이 case들 중 DESTROY case를 제외한 나머지를 살펴보도록 한다.
먼저 선언된 변수들 부터 살펴보겠다. short 형 nVirtKey, 상수변수로 SHIFTED가 선언되었다. 그리고 textmetric 구조체 tm 변수가 선언되었고, DWORD chWidth 및 chHeight 변수가 선언되었다.
- TEXTMETRIC은 폰트와 관련된 데이터를 담고 있는 구조체이다.
- DWORD는 4바이트(32bit) 자료형이다. 32비트 프로세서가 일을 처리할 때 사용하는 기본단위이다.
- 16비트는 WORD이다.
LRESULT WINAPI WinProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
short nVirtKey; // virtual-key code
const short SHIFTED = (short)0x8000;
TEXTMETRIC tm; // structure for text metrics
DWORD chWidth = 20; // width of characters
DWORD chHeight = 20; // height of characters
switch( msg )
{
case WM_CREATE:
// get the text metrics
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
chWidth = tm.tmAveCharWidth; // average character width
chHeight = tm.tmHeight; // character height
return 0;
case WM_DESTROY:
//tell Windows to kill this program
PostQuitMessage(0);
return 0;
case WM_KEYDOWN: // key down
vkKeys[wParam] = true;
switch(wParam)
{
case VK_SHIFT: // shift key
nVirtKey = GetKeyState(VK_LSHIFT); // get state of left shift
if (nVirtKey & SHIFTED) // if left shift
vkKeys[VK_LSHIFT] = true;
nVirtKey = GetKeyState(VK_RSHIFT); // get state of right shift
if (nVirtKey & SHIFTED) // if right shift
vkKeys[VK_RSHIFT] = true;
break;
case VK_CONTROL: // control key
nVirtKey = GetKeyState(VK_LCONTROL);
if (nVirtKey & SHIFTED) // if left control
vkKeys[VK_LCONTROL] = true;
nVirtKey = GetKeyState(VK_RCONTROL);
if (nVirtKey & SHIFTED) // if right control
vkKeys[VK_RCONTROL] = true;
break;
}
InvalidateRect(hwnd, NULL, TRUE); // force WM_PAINT
return 0;
break;
case WM_KEYUP: // key up
vkKeys[wParam] = false;
switch(wParam)
{
case VK_SHIFT: // shift key
nVirtKey = GetKeyState(VK_LSHIFT);
if ((nVirtKey & SHIFTED) == 0) // if left shift
vkKeys[VK_LSHIFT] = false;
nVirtKey = GetKeyState(VK_RSHIFT);
if ((nVirtKey & SHIFTED) == 0) // if right shift
vkKeys[VK_RSHIFT] = false;
break;
case VK_CONTROL: // control key
nVirtKey = GetKeyState(VK_LCONTROL);
if ((nVirtKey & SHIFTED) == 0) // if left control
vkKeys[VK_LCONTROL] = false;
nVirtKey = GetKeyState(VK_RCONTROL);
if ((nVirtKey & SHIFTED) == 0) // if right control
vkKeys[VK_RCONTROL] = false;
break;
}
InvalidateRect(hwnd, NULL, TRUE); // force WM_PAINT
return 0;
break;
case WM_CHAR: // a character was entered by the keyboard
switch (wParam) // the character is in wParam
{
case 0x08: // backspace
case 0x09: // tab
case 0x0A: // linefeed
case 0x0D: // carriage return
case 0x1B: // escape
return 0; // non displayable character
default: // displayable character
ch = (TCHAR) wParam; // get the character
InvalidateRect(hwnd, NULL, TRUE); // force WM_PAINT
return 0;
}
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); // get handle to device context
TextOut(hdc, 0, 0, &ch, 1); // display the character
// Display the state of vkKeys array
// Display 'T' if key is down and 'F' is key is up
for (int r=0; r<16; r++)
{
for (int c=0; c<16; c++)
{
if (vkKeys[r*16+c])
{
SetBkMode(hdc, OPAQUE); // opaque text background
TextOut(hdc,c*chWidth+chWidth*2,r*chHeight+chHeight*2,"T ", 2);
} else {
SetBkMode(hdc, TRANSPARENT); // transparent text background
TextOut(hdc,c*chWidth+chWidth*2,r*chHeight+chHeight*2,"F ", 2);
}
}
}
EndPaint(hwnd, &ps);
return 0;
default:
return DefWindowProc( hwnd, msg, wParam, lParam );
}
}
먼저 WM_CREATE이다.
case WM_CREATE:
// get the text metrics
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
chWidth = tm.tmAveCharWidth; // average character width
chHeight = tm.tmHeight; // character height
return 0;
여기서 GETDC ~ ReleaseDC로 이루어지는데, 이 DC(Device Context)는 윈도우즈에서 그리기 작업을 할 때 필요하다.
GetDC와 ReleaseDC
1. 먼저 봐야 할 내용들이 글은 아래에 링크한 내용을 먼저 읽어야만 이해할 수 있습니다.Bitmap과 GDI...
blog.naver.com
(참고자료)
getTextMetrics는 핸들의 textmetrix값을 tm에 반환해주게 된다.
그리고 chWidth에 평균 캐릭터의 폭을 넣어주고, chHeight에 문자 높이를 넣어주게 된다.
이렇게 되면 윈도우 창이 생성 되었을때, 필요한 정보들을 얻을 수 있다.
WM_KEYDOWN이다.
case WM_KEYDOWN: // key down
vkKeys[wParam] = true;
switch(wParam)
{
case VK_SHIFT: // shift key
nVirtKey = GetKeyState(VK_LSHIFT); // get state of left shift
if (nVirtKey & SHIFTED) // if left shift
vkKeys[VK_LSHIFT] = true;
nVirtKey = GetKeyState(VK_RSHIFT); // get state of right shift
if (nVirtKey & SHIFTED) // if right shift
vkKeys[VK_RSHIFT] = true;
break;
case VK_CONTROL: // control key
nVirtKey = GetKeyState(VK_LCONTROL);
if (nVirtKey & SHIFTED) // if left control
vkKeys[VK_LCONTROL] = true;
nVirtKey = GetKeyState(VK_RCONTROL);
if (nVirtKey & SHIFTED) // if right control
vkKeys[VK_RCONTROL] = true;
break;
}
InvalidateRect(hwnd, NULL, TRUE); // force WM_PAINT
return 0;
break;
키가 눌렸을때, 전달된 메세지의 값인 wParam 인덱스의 vkKey를 true로 초기화 한다. 그리고 스위치문에서 ctrl이나 shift가 눌렸는지를 판단한다. 그리고 쉬프트가 윈쪽 쉬프트인지 오른쪽 쉬프트인지 확인하게 된다. 그 확인을 (GetKeyState(VK_LSHIFT or VK_RSHIFT))로 하게 된다.
WM_KEYUP 케이스이다.
case WM_KEYUP: // key up
vkKeys[wParam] = false;
switch(wParam)
{
case VK_SHIFT: // shift key
nVirtKey = GetKeyState(VK_LSHIFT);
if ((nVirtKey & SHIFTED) == 0) // if left shift
vkKeys[VK_LSHIFT] = false;
nVirtKey = GetKeyState(VK_RSHIFT);
if ((nVirtKey & SHIFTED) == 0) // if right shift
vkKeys[VK_RSHIFT] = false;
break;
case VK_CONTROL: // control key
nVirtKey = GetKeyState(VK_LCONTROL);
if ((nVirtKey & SHIFTED) == 0) // if left control
vkKeys[VK_LCONTROL] = false;
nVirtKey = GetKeyState(VK_RCONTROL);
if ((nVirtKey & SHIFTED) == 0) // if right control
vkKeys[VK_RCONTROL] = false;
break;
}
InvalidateRect(hwnd, NULL, TRUE); // force WM_PAINT
return 0;
break;
사실 이 WM_KEYUP 함수의 대체적인 동작은 WM_KEYDOWN과 거의 동일하다. 마지막 InvalidateRect()함수를 호출시키는 것만 다른데, 초기화할 영역을 NULL 인자로 호출하고 있다. 이 함수를 호출하면 WM_PAINT가 확인하고 화면을 다시 그려줄 수 있도록 플래그를 표시해 놓는다. 메세지를 윈도우즈가 모두 처리하고 난 후 WM_PAINT가 플래그를 확인하여 작업을 수행해주게 된다.
WM_CHAR이다.
case WM_CHAR: // a character was entered by the keyboard
switch (wParam) // the character is in wParam
{
case 0x08: // backspace
case 0x09: // tab
case 0x0A: // linefeed
case 0x0D: // carriage return
case 0x1B: // escape
return 0; // non displayable character
default: // displayable character
ch = (TCHAR) wParam; // get the character
InvalidateRect(hwnd, NULL, TRUE); // force WM_PAINT
return 0;
}
이 케이스는 키보드에서 입력을 했을 때 수행된다. 키보드의 값을 case문으로 처리하는데 backspace나 tab같은 특수한 키들을 제외하고, 일반적인 문자의 경우 ch에 담겨진다.
실제 수행결과에 backspace를 누르면 9번째 칸(0부터 시작하므로)이 True로 변하는 것을 확인할 수 있다.
WM_PAINT이다.
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); // get handle to device context
TextOut(hdc, 0, 0, &ch, 1); // display the character
// Display the state of vkKeys array
// Display 'T' if key is down and 'F' is key is up
for (int r=0; r<16; r++)
{
for (int c=0; c<16; c++)
{
if (vkKeys[r*16+c])
{
SetBkMode(hdc, OPAQUE); // opaque text background
TextOut(hdc,c*chWidth+chWidth*2,r*chHeight+chHeight*2,"T ", 2);
} else {
SetBkMode(hdc, TRANSPARENT); // transparent text background
TextOut(hdc,c*chWidth+chWidth*2,r*chHeight+chHeight*2,"F ", 2);
}
}
}
EndPaint(hwnd, &ps);
return 0;
처음 TextOut함수는 가장 왼쪽 상단에 키를 일반적인 문자를 누르면 표시되는 네모 박스를 표현해준다.
그 후에 16*16 배열의 구성을 반복문으로 출력하는데, SetBKMode는 문자의 배경을 투명하게 할 것인지 불투명하게 할 것인지 선택하는 것이다.(opaque가 불투명이다.) 그리고 chWidth를 통해서 위치를 계산하고 T거나 F를 출력해주게 된다.
그 후 작업이 끝나면 EndPaint를 호출하게 된다.
CreateWindow는 동일하므로 생략.
'윈도우 프로그래밍(DirectX)' 카테고리의 다른 글
[2]Character input - 2D game programming (0) | 2020.09.09 |
---|---|
[1]Hello, World - 2D game Programming (0) | 2020.09.05 |