
반응형
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의 좌표 증감 연산을 진행하면 보다 좋은 코드가 될 것 같다.
반응형