profile image

L o a d i n g . . .

article thumbnail image
Published 2020. 5. 27. 22:45
반응형

1. winAPI 기초

#include <Windows.h>

//API : Application Programming Interface

HINSTANCE _hInstance;
HWND	  _hWnd;
LPCTSTR _lpszClass = TEXT("21");

//함수 프로토타입 선언
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

/*
HINSTANCE hInstance: 프로세스의 시작 주소, 시작 주소를 알 수 있으므로 프로그램 내 자원을 사용할 때 사용함
HINSTANCE hPrevInstance: 이전에 실행되었던 프로그램의 주소
LPSTR lpszCmdParam: 프로그램 시작 시 실행 파일 뒤에 적힌 값을 넘겨 받을 수 있음, 실행과 동시에 값을 빠르게 넣을 수 있음
int cmdShow: 실행 시 창의 크기를 조절할 옵션을 저장함

API에서 H는 Handle, pointer는 주소로 접근하기 때문에 운영체제의 자원을 침범할 수 있어 이를 방지하기 위해 도입됨

접두어 lpsz: NULL 종료 문자열의 포인터(문자열 맨 끝이 NULL인 문자열)
*/
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int cmdShow)
{
	
	MSG		message;	//윈도우 메시지를 담은 구조체
	WNDCLASS	wndClass;	//윈도우 클래스 구조체

	_hInstance = hInstance;

	wndClass.cbClsExtra = 0;	// 윈도우 안에 윈도우를 넣기
	wndClass.cbWndExtra = 0;	// 윈도우 안에 윈도우를 넣기
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	// 윈도우 배경색, Stock: 운영체제가 만들어 놓은 자원
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);		// 마우스 커서
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);	// 프로그램 아이콘 설정
	wndClass.hInstance = hInstance;
	wndClass.lpfnWndProc = (WNDPROC)WndProc;	// 콜백 함수 전달
	wndClass.lpszClassName = _lpszClass;	// 윈도우 이름
	wndClass.lpszMenuName = NULL;	// 추가할 메뉴 이름
	wndClass.style = CS_HREDRAW | CS_VREDRAW;	// 여러 기능(다른 창과 겹칠 때, 최소화할 때...)을 define한 값

	RegisterClass(&wndClass);	// 설정한 WNDCLASS를 등록, 등록하지 않으면 서비스를 받을 수 없음

	_hWnd = CreateWindow(
		_lpszClass,	// 이름
		_lpszClass,	// 캡션바 이름
		WS_OVERLAPPEDWINDOW,	// 윈도우 스타일
		50,	// 시작 위치
		50,
		800,	// 창 크기
		600,
		NULL,	// 부모 윈도우
		(HMENU)NULL,	// 사용자 메뉴
		hInstance,
		NULL	// 고유값
	);

	ShowWindow(_hWnd, cmdShow);

	// 메시지가 올 때까지 프로세스는 대기 중, 메시지를 받으면 동작
	while (GetMessage(&message, 0, 0, 0))	// 메시지를 꺼냄
	{
		TranslateMessage(&message);	// 여러 정보를 운영체제가 계산함
		DispatchMessage(&message);	// 메시지를 실행
	}

	return message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	//HWND: 핸들 메시지, 어떤 윈도우에서 메시지가 발생했는지
	//UINT: 메시지 발생 번호, 어떤 메시지인지
	//WPARAM: 키보드 입력 및 마우스 클릭 메시지 전달
	//LPARAM: 마우스 좌표 전달

	//TODO: 조사해 보기
	PAINTSTRUCT ps;
	HDC			hdc;	// Device Context

	switch (iMessage)
	{
	case WM_PAINT:
	{
		hdc = BeginPaint(hWnd, &ps);
		// 이 사이에 hdc를 이용하여 그릴 수 있다.
		/*
		// 기본적으로 2012년 이후, ms는 유니코드를 권장한다.

		// 화면에 텍스트를 출력한다.
		// wcslen: strlen의 와이드바이트 버전이라고 보면 됨
		TextOut(hdc, 100, 100, L"2D 실화냐", wcslen(L"2D 실화냐"));
		TextOut(hdc, 200, 100, L"공부하자", wcslen(L"공부하자"));
		TextOut(hdc, 150, 150, L"살려주세여", wcslen(L"살려주세여"));

		// 텍스트에 컬러를 입힌다.
		// RGB color, unsigned byte(0~255)
		SetTextColor(hdc, RGB(0, 255, 100));
		TextOut(hdc, 300, 300, L"으악", wcslen(L"으악"));
		TextOut(hdc, 300, 350, L"초록색", wcslen(L"초록색"));

		SetTextColor(hdc, RGB(255, 0, 255));
		const TCHAR* str = TEXT("아이스 아메리카노");
		TextOut(hdc, 200, 200, str, wcslen(str));
		*/

		// 멀티바이트 환경
		TextOut(hdc, 50, 50, "편해졌다", strlen("편해졌다"));

		const char* str = "와 10 30분 넘었네 곧 3교시";
		TextOut(hdc, 100, 100, str, strlen(str));

		//멀티바이트	 와이드바이트		유니코드
		//strlen	==	wcslen	==	_tcslen	(문자열 길이)
		//strcmp	==	wcscmp	==	_tcscmp	(문자열 비교)
		//strcpy	==	wcscpy	==	_tcscpy	(문자열 복사)
		//strchr	==	wcschr	==	_tcschr	(문자 찾기)
		//strstr	==	wcsstr	==	_tcsstr	(문자열 찾기)
		//strcat	==	wcscat	==	_tcscat	(문자열 이어붙이기)
		//strtok	==	wcstok	==	_tcstok	(문자열 자르기)

		EndPaint(hWnd, &ps);
	}
		break;
	case WM_DESTROY:	// 종료 버튼을 누르면
		PostQuitMessage(0);
		break;
	}

	return (DefWindowProc(hWnd, iMessage, wParam, lParam));
}

 

2. 화면에 점, 선, 원, 사각형 그리기

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;

	switch (iMessage)
	{
	case WM_PAINT:
	{
		hdc = BeginPaint(hWnd, &ps);
        
        // 픽셀을 찍는다.
		SetPixel(hdc, 50, 50, RGB(255, 0, 0));

		// 선을 긋는다.
		//  (그려줄DC,   X,   Y, 포인트구조체)
		MoveToEx(hdc, 100, 100, NULL);	// 시작점 지정
		LineTo(hdc, 200, 200);	// 해당 좌표 목적지까지 선 긋기
		LineTo(hdc, 300, 100);	// 200, 200에서 다시 선 긋기

		MoveToEx(hdc, 400, 400, NULL);
		LineTo(hdc, 100, 500);

		//Rectangle - 사각형 그리기
		//연속적으로 그리게 되면 가려진다.
		Rectangle(hdc, 100, 100, 200, 200);
		Rectangle(hdc, 150, 150, 350, 350);

		// Ellipse - 원 그리기
		// 사각형 영역만큼 원이 그려진다.
		Ellipse(hdc, 150, 150, 350, 350);
        
		EndPaint(hWnd, &ps);
	}
	break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	}

	return (DefWindowProc(hWnd, iMessage, wParam, lParam));
}

 

3. HBRUSH, HPEN

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	//HWND - 핸들메시지
	//UINT - 메시지 발생 번호
	//WPARAM - 키보드 입력 및 마우스 클릭 메시지 전달
	//LPARAM - 마우스 좌표 전달

	PAINTSTRUCT ps;
	HDC	hdc;	//Device Context

	switch (iMessage)
	{
	case WM_PAINT:
	{
		hdc = BeginPaint(hWnd, &ps);

		// SelectObject()는 이전 BRUSH를 반환한다.
		HBRUSH brush = CreateSolidBrush(RGB(33, 33, 77));
		HBRUSH oldBrush = (HBRUSH)SelectObject(hdc, brush);

		Ellipse(hdc, 150, 150, 350, 350);

		SelectObject(hdc, oldBrush);
		DeleteObject(brush);

		HPEN pen = CreatePen(PS_SOLID, 10, RGB(0, 0, 255));
		HPEN oldPen = (HPEN)SelectObject(hdc, pen);

		MoveToEx(hdc, 100, 100, NULL);
		LineTo(hdc, 300, 300);

		SelectObject(hdc, oldPen);
		DeleteObject(pen);

		EndPaint(hWnd, &ps);
	}
	break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	}

	return (DefWindowProc(hWnd, iMessage, wParam, lParam));
}

 

4. 영역 재조정

void setWindowsSize(int x, int y, int width, int height)
{
	RECT winRect;

	winRect.left = 0;
	winRect.top = 0;
	winRect.right = width;
	winRect.bottom = height;

	AdjustWindowRect(&winRect, WINSTYLE, false);

	//실질적으로 클라이언트 영역 크기 셋팅 해주는 함수
	SetWindowPos(_hWnd, NULL, x, y,
		(winRect.right - winRect.left),
		(winRect.bottom - winRect.top),
		SWP_NOZORDER | SWP_NOMOVE);
}

 

5. 키보드 입력 처리

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	//HWND - 핸들메시지
	//UINT - 메시지 발생 번호
	//WPARAM - 키보드 입력 및 마우스 클릭 메시지 전달
	//LPARAM - 마우스 좌표 전달

	PAINTSTRUCT ps;
	HDC	hdc;	//Device Context

	static RECT _rc;	// 콜백 함수 안에서 하면 안 되는 듯,, static으로 전역 변수 선언함
	static RECT _rc_m;	// 작은 사각형
	static RECT _rc2;	

	switch (iMessage)
	{
		// 윈도우 창이 켜질 때(생성자 같은 것)
	case WM_CREATE:
	{
		_rc = RectMakeCenter(WINSIZEX / 2 - 100, WINSIZEY / 2, 100, 100);
		_rc_m = RectMakeCenter(WINSIZEX / 2 - 100, WINSIZEY / 2, 50, 50);
		_rc2 = RectMakeCenter(WINSIZEX / 2 + 100, WINSIZEY / 2, 100, 100);
	}
		break;
	case WM_PAINT:
	{
		hdc = BeginPaint(hWnd, &ps);

		Rectangle(hdc, _rc.left, _rc.top, _rc.right, _rc.bottom);
		Rectangle(hdc, _rc_m.left, _rc_m.top, _rc_m.right, _rc_m.bottom);
		Rectangle(hdc, _rc2.left, _rc2.top, _rc2.right, _rc2.bottom);

		EndPaint(hWnd, &ps);
	}
	break;
	case WM_KEYDOWN:
	{
		switch (wParam)
		{
		case VK_ESCAPE:
		// Esc키가 눌렸을 경우
			PostQuitMessage(0);
			break;
		case VK_LEFT:
			/*
			왼쪽 방향키가 눌렸을 경우
			RECT _rc 및 _rc2 조정
			...
			*/
			// 윈도우의 일부분을 무효화함
			InvalidateRect(_hWnd, NULL, true);
			break;
		case VK_RIGHT:
			/*
			오른쪽 방향키가 눌렸을 경우
			...
			*/
			InvalidateRect(_hWnd, NULL, true);
			break;
		case VK_UP:
			/*
			위쪽 방향키가 눌렸을 경우
			...
			*/
			InvalidateRect(_hWnd, NULL, true);
			break;
		case VK_DOWN:
			/*
			아래쪽 방향키가 눌렸을 경우
			...
			*/
			InvalidateRect(_hWnd, NULL, true);
			break;
		}
	}
	break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	}

	return (DefWindowProc(hWnd, iMessage, wParam, lParam));
}

 

6. 하드코딩 수정

첫 번째 사각형을 방향키로 움직여서 다른 사각형을 미는 코드를 다음과 같이 작성하였다.

case VK_LEFT:
	if (_rc.left <= 0) break;
	if (_rc.left <= _rc2.right && _rc.left > _rc2.left && _rc.bottom > _rc2.top && _rc.top < _rc2.bottom)
	{
		if (_rc2.left <= 0) break;
		_rc2.left -= 5;
		_rc2.right -= 5;
	}
	if (_rc.right <= _rc_m.right)
	{
		_rc_m.left -= 5;
		_rc_m.right -= 5;
	}
	_rc.left -= 5;
	_rc.right -= 5;

	InvalidateRect(_hWnd, NULL, true);
	break;

조절할 수 있는 사각형은 그렇다 치고, 밀리는 사각형마저 직접 5를 더하고 빼는 연산을 하는 게 마음에 걸렸지만 어떻게 손을 봐야 할지 감이 안 왔었다. 다른 코드를 보고 감을 잡아서 메모해 놓으려고 한다.

두 사각형이 닿았을 때, r1.right와 r2.right는 사각형의 너비(right - left)만큼의 간격을 유지할 것이다. 이것을 이용하여 _r2의 좌표 증감 연산을 진행하면 보다 좋은 코드가 될 것 같다.

반응형
복사했습니다!