본문 바로가기

デベロップメント/ゲームプログラミング

2.4.1 WM_CHAR 메세지

2.4.1 WM_CHAR 메세지


Windows가 키의 누름에 연동해서 송신하는 메세지는 어느정도 있습니다.
문자를 읽어들이는 것에는 WM_CHAR 메세지를 사용합니다
WM_CHAR 메세지는 유저가 키보드에 문자를 1개 타이핑 한 것을 게임에 송신됩니다.
타이핑된 문자를 읽어들이기 위해 필요한 것은, WM_CHAR 메세지의 핸들러를 WinProc 함수에 추가하는 것입니다.(#코드 2.11)

#코드 2.11 WM_CHAR 메세지의 핸들러
case WM_CHAR:							// 키보드에서 문자가 입력 된 경우
	switch(wParam){						// 문자는 wParam에 저장되어 있음
		case 0x08:					// 백 스페이스
		case 0x09:					// 탭
		case 0x0A:					// LF(Line Feed)
		case 0x0D:					// CR(Carriage Return)
		case 0x1B:					// ESC
			MessageBeep((UINT) -1);			// 비프음을 내고, 표시는 하지 않음
			return 0;
		default:					// 표시가능한 문자
			ch = (TCHAR)wParam;			// 문자를 취득
			InvalidateRect(hwnd, NULL, TRUE);	// WM_PAINT를 강제적으로 발생시킴
			return 0;
	}

wParam에는, 눌린 키의 문자 코드가 저장되어 있습니다.
그 문자가 표시 불가한가를 체크해서, 표시 불가의 경우에는, 키의 눌림을 인식한 것을 알리기 위해 단순히 비프음을 내도록 Windows에 지시합니다.
눌린 키가 표시가능한 문자의 경우는, wParam에서 값을 읽어들여, 그것을 변수 ch에 보존합니다.

여기서, 단순히 문자를 화면에 표시해봅시다.
이를 위해서는, WinProc로 WM_PAINT 메세지를 감지합니다.
WM_PAINT 메세지는, Windows가 윈도우의 전체 또는 일부를 다시 그리는 것을 할 필요가 있다고 판단할 때에 송신됩니다.
예를 들어, 윈도우의 사이즈를 변경할 때
WM_PAINT 이벤트를 강제적으로 즉시 발생시키기 위해, WM_CHAR 메세지 코드의 마지막에 InvalidateRect 함수를 호출합니다.

윈도우에 텍스트를 표시 하는데에는, 그 앞에 디바이스 컨텍스트를 취득해 둘 필요가 있습니다.
BeginPaint 함수는 윈도우가 묘화를 할 때에 표준을 갖은, 디바이스 컨텍스트에 대한 핸들을 반환합니다.
핸들이라는 것은, 별도의 오브젝트를 참조할 때에 사용하는 Windows 데이터 형입니다.
디바이스 컨텍스트에 대한 핸들을 보유하기 위해, #코드 2.12와 같이, 전용의 변수를 작성합니다.

#코드 2.12 디바이스 컨텍스트에 대한 핸들
HDC hdc;	// 디바이스 컨텍스트에 대한 핸들

텍스트는 TextOut 함수를 사용해 윈도우에 표시할 수 있습니다.
이번 장에서는 텍스트 입력의 코드를 테스트하는 것을 목적으로, 단순히 TextOut을 사용합니다.
후술의 장에서는 다른 방법을 사용하여 게임 내에 텍스트를 표시합니다.
TextOut의 처음의 파라메터는 hdc 변수입니다.
#코드 2.13에 보여지고 있는 코드를 WinProc 함수에 추가합니다.

#코드 2.13 텍스트의 표시
case WM_PAINT:				// 윈도우를 재묘화할 필요가 있는 경우
	hdc = BeginPaint(hwnd, &ps);	// 디바이스 컨텍스트에 대한 핸들을 취득
	GetClientRect(hwnd, &rect);	// 윈도우 사각의 크기를 취득
	
	// 문자를 표시
	TextOut(hdc, rect.right/2, rect.bottom/2, &ch, 1);
	EndPaint(hwnd, &ps);
	return 0;

#그림 2.7은 「X」가 눌렸을 때의 프로그램에서의 출력입니다.
#코드 2.14에, 「Character Input」 샘플의 완전한 코드를 봅시다.

#그림 2.7 「Character Input」 샘플

#코드 2.14 「Character Input」 샘플
// Programming 2D Games
// Copyright (c) 2011 by:
// Charles Kelly
// 제 2장 Windows 스타일의 「Hello World」 v1.0
// winmain.cpp
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

// 함수 프로토타입
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
bool CreateMainWindow(HINSTANCE, int);
LRESULT WINAPI WinProc(HWND, UINT, WPARAM, LPARAM);

// 전역 변수
HINSTANCE hinst;
HDC hdc;			// 디바이스 컨텍스트에 대한 핸들
TCHAR ch = ' ';		// 입력된 문자
RECT rect;			// Rectangle 구조체
PAINTSTRUCT ps;		// WM_PAINT에서 사용됨

// 정수
const char CLASS_NAME[] = "WinMain"; 
const char APP_TITLE[] = "Hello World";
const int WINDOW_WIDTH = 400;		// 윈도우의 너비
const int WINDOW_HEIGHT = 400;		// 윈도우의 높이

// ==================================================================================================
// Windows 어플리케이션의 시작점
// ==================================================================================================
int WINAPI WinMain(HINSTANCE hInstance,
					HINSTANCE hPrevInstance,
					LPSTR lpCmdLine,
					int nCmdShow) {
	MSG msg;

	// 윈도우를 작성
	if (!CreateMainWindow(hInstance, nCmdShow)) return false;

	// 메인의 메세지 루프
	int done = 0;
	while (!done) {
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
			
			// 종료 메세지 감지
			if (msg.message == WM_QUIT) done = 1;

			// 윈도우 이벤트 콜 백 함수
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return msg.wParam;
}

// ==================================================================================================
// 윈도우 이벤트 콜 백 함수
// ==================================================================================================
LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	switch (msg) {
	case WM_DESTROY:
		
		// Windows에 이 프로그램을 종료하도록 전함
		PostQuitMessage(0);
		return 0;
	case WM_CHAR:							// 키보드에서 문자가 입력 된 경우
		switch (wParam) {						// 문자는 wParam에 저장되어 있음
		case 0x08:					// 백 스페이스
		case 0x09:					// 탭
		case 0x0A:					// LF(Line Feed)
		case 0x0D:					// CR(Carriage Return)
		case 0x1B:					// ESC
			MessageBeep((UINT)-1);			// 비프음을 내고, 표시는 하지 않음
			return 0;
		default:					// 표시가능한 문자
			ch = (TCHAR)wParam;			// 문자를 취득
			InvalidateRect(hwnd, NULL, TRUE);	// WM_PAINT를 강제적으로 발생시킴
			return 0;
		}
	case WM_PAINT:				// 윈도우를 재묘화할 필요가 있는 경우
								// 디바이스 컨텍스트에 대한 핸들을 취득
		hdc = BeginPaint(hwnd, &ps);	
		GetClientRect(hwnd, &rect);	// 윈도우 사각의 크기를 취득

		// 문자를 표시
		TextOut(hdc, rect.right / 2, rect.bottom / 2, &ch, 1);
		EndPaint(hwnd, &ps);
		return 0;

	default:
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}

	return DefWindowProc(hwnd, msg, wParam, lParam);
}

// ==================================================================================================
// 윈도우를 작성
// 반환 값 : 에러시의 경우, false
// ==================================================================================================
bool CreateMainWindow(HINSTANCE hInstance, int nCmdShow) {
	WNDCLASSEX wcx;
	HWND hwnd;

	// 윈도우 클래스의 구조체를
	// 메인 윈도우를 기술하는 파라메터로 설정합니다.
	wcx.cbSize = sizeof(wcx);									// 구조체의 사이즈
	wcx.style = CS_HREDRAW | CS_VREDRAW;						// 윈도우 사이즈 변경 시에 재묘화
	wcx.lpfnWndProc = WinProc;									// 윈도우 프로시져를 가르킴
	wcx.cbClsExtra = 0;											// 확장 클래스 메모리 없음
	wcx.cbWndExtra = 0;											// 확장 윈도우 메모리 없음
	wcx.hInstance = hInstance;									// 인스턴스에 대한 핸들
	wcx.hIcon = NULL;
	wcx.hCursor = LoadCursor(NULL, IDC_ARROW);					// 사전 정의되어 있는 마우스 커서
	wcx.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);	// 흑색의 배경
	wcx.lpszMenuName = NULL;									// 메뉴 리소스의 이름
	wcx.lpszClassName = CLASS_NAME;								// 윈도우 클래스의 이름
	wcx.hIconSm = NULL;											// 작은 아이콘

	// 윈도우 클래스를 등록
	// 에러 시, RegisterClassEx는 0을 반환
	if (RegisterClassEx(&wcx) == 0) return false; // 에러 시에
	
	hwnd = CreateWindow(CLASS_NAME,				// 윈도우 클래스의 이름
						APP_TITLE,				// 타이틀 바의 텍스트
						WS_OVERLAPPEDWINDOW,	// 윈도우의 스타일
						CW_USEDEFAULT,			// 윈도우의 수평 위치의 디폴트
						CW_USEDEFAULT,			// 윈도우의 수직 위치의 디폴트
						WINDOW_WIDTH,			// 윈도우의 너비
						WINDOW_HEIGHT,			// 윈도우의 높이
						(HWND)NULL,				// 부모 윈도우 없음
						(HMENU)NULL,			// 메뉴 없음
						hInstance,				// 어플리케이션 인스턴스에 대한 핸들
						(LPVOID)NULL);			// 윈도우 파라메터 없음

	// 윈도우의 작성에 에러가 발생 했을 경우
	if (!hwnd) return false;

	// 윈도우를 표시
	ShowWindow(hwnd, nCmdShow);

	// 윈도우 프로시져에 WM_PAINT 메세지를 보냄
	UpdateWindow(hwnd);
	return true;
}


서장으로