WIN32 Singleton
카테고리: WINAPI
4일차
오늘부턴 WINAPI의 구조를 바꿀 것이다.
- Default
- Client.h
- framework.h
- Resouce.h
- targetver.h
- Engine
- Header
- define.h
- Core
- CCore.cpp
- CCore.h
- Manager
- KeyMgr
- CkeyMgr.cpp
- CkeyMgr.h
- KeyMgr
- Header
- 리소스 파일
- main.cpp
- pch.h.
형태로 구성된다.
- pch.h는 프로젝트의 미리 컴파일된 파일로 설정해놓는다.
#pragma once
//미리 컴파일된 헤더.
//이미 제공된 애들 <- 바뀔 여지가 없으니까, 다시 컴파일 할 이유X
//따라서 이걸 사용하면, 컴파일 속도가 빨라짐.
//그리고 클래스를 만들때 선언해둔 미리 컴파일된 헤더 생김.
#include <Windows.h>
#include "define.h"
- define.h
#pragma once
//매크로는 코드를 치환하는 것.
//따라서 함수를 호출한 적이 없어서 스위칭이 없어서 더 빠름.
//주의점은 매크로는 함수가 아니라, 그대로 코드를 치환하는거라서
//완전히 다른 느낌이 될 수 있음...
//이렇게 하면 내가 해제를 신경쓰지 않아도 된다는 점
//반대로 이렇게 하면의 단점.
//끝까지 끌고 나가야 하는 방식.
//SingleTon 매크로.
#define SINGLE(type)public: \
static type* GetInstance()\
{\
static type mgr;\
return &mgr;\
}
- CCore.h
#pragma once
//코어가 되는 객체는 하나만 있어야 하지 않을까?
//싱글톤 패턴. 객체의 생성을 1개로 제한.
//어디서든 쉽게 접근 가능
//객체를 만들어주는 함수, 자기 스스로를 만들어주는 멤버 함수
/*
class CCore
{
//정적 멤버(데이터 영역)
//객체를 힙이나, 스택이나, 데이터에 어찌됐던 만듬.
//데이터 영역에 하나만 있음. 데이터 멤버는 초기화를 반드시 해줘야함.
static CCore* g_pInst;
public:
//멤버 함수를 호출하려면 객체를 호출해야 하는 것 아니냐?
//그래서 사용하는 건 static 함수.
//스태틱은 객체가 있어도 없어도 호출 가능.
//객체로 static함수 써도 딱히 의미가 없음.
//static함수는, 멤버에 접근 불가(this키워드 불가)
// 그런데 유일하게 접근 가능한 것. 정적 멤버 가능.
// 정적 변수는 데이터 영역에 만들어짐.
//this란 호출한 객체를 의미하는 것.
static CCore* GetInstance() {
if (nullptr == g_pInst) {
g_pInst = new CCore;
}
return g_pInst;
}
static void Release() {
if (nullptr != g_pInst) {
delete g_pInst;
g_pInst = nullptr;
}
}
private:
//1번, 생성자 소멸자를 숨겨서 외부에서 못 만들도록.
CCore();
~CCore();
};
*/
//이 밑은 완전 새로운 방식임.
#include "define.h"
class CCore {
SINGLE(CCore);
private:
HWND m_hWnd; //메인 윈도우 핸들.
POINT m_ptResolution; //메인 윈도우 해상도
public:
int init(HWND _hWnd, POINT _ptResolution);
void progress();
private:
CCore();
~CCore();
};
//CCore.cpp
#include "pch.h"
#include "CCore.h"
//CCore* CCore::g_pInst = nullptr;
CCore::CCore() {
}
CCore::~CCore() {
}
int CCore::init(HWND _hWnd, POINT _ptResolution) {
m_hWnd = _hWnd;
m_ptResolution = _ptResolution;
//해상도에 맞게 윈도우 크기 조정.
return S_OK;
}
void CCore::progress() {
}
- CKeyMgr
//.cpp파일
#include "pch.h"
#include "CCore.h"
//CCore* CCore::g_pInst = nullptr;
CCore::CCore() {
}
CCore::~CCore() {
}
int CCore::init(HWND _hWnd, POINT _ptResolution) {
m_hWnd = _hWnd;
m_ptResolution = _ptResolution;
//해상도에 맞게 윈도우 크기 조정.
return S_OK;
}
void CCore::progress() {
}
//.h파일
#pragma once
//#include 거는 거 불편함. 그래서.
#include "define.h"
class CkeyMgr
{
SINGLE(CkeyMgr);
};
- main.cpp파일
// Client.cpp : 애플리케이션에 대한 진입점을 정의합니다.
//
#include "pch.h"
#include "framework.h"
#include "Client.h"
#include "CCore.h"
#define MAX_LOADSTRING 100
//WCHAR은 2바이트, wchar_t형임. (16비트)
//그거 이외엔 다른 게 없음(유니코드형을 위한 것)
// 전역 변수:
HINSTANCE hInst; // 현재 인스턴스입니다.
//메인함수가 처음 시작할 때 받아오는 값.
HWND g_hWnd; //메인 윈도우 핸들.
WCHAR szTitle[MAX_LOADSTRING]; // 제목 표시줄 텍스트입니다.
WCHAR szWindowClass[MAX_LOADSTRING]; // 기본 창 클래스 이름입니다.
//100길이로 그냥 선언해놓은 것.
// 이 코드 모듈에 포함된 함수의 선언을 전달합니다:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
//함수들은 전방 선언해놓은 것.(함수 프로토타입)
#include <vector>
using std::vector;
struct tObjInfo {
POINT g_ptObjectPos;
POINT g_ptObjScale;
};
vector<tObjInfo> g_vecInfo;
//좌 상단
POINT g_ptLT;
//우 하단.
POINT g_ptRB;
bool bLbtnDown = false;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
//딱히 신경쓸 건 없음. 얘내 둘은 딱히 쓰이지 않는다... 라는 걸 선언한 느낌.
//매크로임.
// TODO: 여기에 코드를 입력합니다.
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_CLIENT, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 애플리케이션 초기화를 수행합니다:
// 여기서 윈도우 창을 생성함. 유저와 프로그램 사이의 인터페이스, 접점이라는 의미도 됨.
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
//CCore 초기화
//이게 메인 윈도우 입니다. 이게 해상도입니다.
if (FAILED(CCore::GetInstance()->init(g_hWnd, POINT{1280, 768}))) {
MessageBox(nullptr, L"Core 객체 초기화 실패", L"ERROR", MB_OK);
return FALSE;
}
//단축키 정보.
//리소스 뷰 -> Accelerator -> table
//특정 키와 조합했을 때, 키값.
//테이블 정보 불러와서.
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLIENT));
MSG msg;
while (true)
{
//무한 반복 -> 메시지가 없으면 if문 바깥으로 무시.
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
//메시지를 처리하기 전에,
if (WM_QUIT == msg.message) {
break;
}
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//메시지가 발생하지 않는 대부분의 시간.
else {
//디자인 패턴(설계 유형)
//싱글톤 패턴
CCore::GetInstance()->progress();
}
}
//두 번재 매개변수가 ID.
return (int)msg.wParam;
}
//
// 함수: MyRegisterClass()
//
// 용도: 창 클래스를 등록합니다.
// 창 클래스를 우리가 정의하고, 등록하는 절차는 윈도우 OS에서 제공하는 함수.
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
//구조체 채워서 함수에 전달하기.
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
//WndProc이란? 전방 선언 되어 있는 위 함수.
//함수 포인터.
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CLIENT));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
//처음 생성되는 메뉴바 null 값을 넣으면 메뉴바 사라짐.
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_CLIENT);
//이거의 Key값을 szWindowClass로 사용함. 이 MyRegisterClass의 Key값임.
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
//이건 윈도우 OS에서 제공하는 함수임...
return RegisterClassExW(&wcex);
}
//
// 함수: InitInstance(HINSTANCE, int)
//
// 용도: 인스턴스 핸들을 저장하고 주 창을 만듭니다.
//
// 주석:
//
// 이 함수를 통해 인스턴스 핸들을 전역 변수에 저장하고
// 주 프로그램 창을 만든 다음 표시합니다.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.
//szTitle 대신 "한재훈~" 넣어도 됨. 2번째 변수는 이름 말하는 거임.
//szWindowClass 키 값대로 만들어달라.
g_hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!g_hWnd)
{
return FALSE;
}
ShowWindow(g_hWnd, nCmdShow);
UpdateWindow(g_hWnd);
return TRUE;
}
//
// 함수: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 용도: 주 창의 메시지를 처리합니다.
//
// WM_COMMAND - 애플리케이션 메뉴를 처리합니다.
// WM_PAINT - 주 창을 그립니다.
// WM_DESTROY - 종료 메시지를 게시하고 반환합니다.
//
// 메시지가 발생한 윈도우 id값, 메시지등등이 같이 들어옴.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND://왠만한 메시지(단축키)는 이렇게 여기에서 처리함.
{
int wmId = LOWORD(wParam);
// 메뉴 선택을 구문 분석합니다:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps); //Devicde Context.(그리기)
HPEN hRedPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
//자주 쓰는 펜, 자주 쓰는 오브젝트는 이미 다 만들어놨음.
HBRUSH hBlueBrush = CreateSolidBrush(RGB(0, 0, 255));
HPEN hDefaultPen = static_cast<HPEN>(SelectObject(hdc, hRedPen));
HBRUSH hDefaultBrush = static_cast<HBRUSH>(SelectObject(hdc, hBlueBrush));
//3일차. 여러 개의 물체가 있다면???
// 물체마다, 좌표가 달라서... 한 물체가 움직이면 다른 물체에게 영향을 미칠 수 있어서 전체적으로 재갱신을 해줘야만 함.
//게임이란 렌더링, 화면을 매 순간 적용하기 위해 화면을 계속 그리는 반복의 연속임.(프레임이라는 개념)
//변경된 펜으로 사각형 그림.
if (bLbtnDown) {
Rectangle(hdc, g_ptLT.x, g_ptLT.y, g_ptRB.x, g_ptRB.y);
}
//물체가 늘어나면 늘어날수록 화면 깜빡임이 되게 심해짐...
//왜 그럴까? 마우스만 움직여도 InvalidateRect로 계속 다시 그림. 깜빡임 현상은 컴퓨터의 속도가 중요한 게 아님...
//이건 속도의 문제가 아니라 타이밍의 문제임. 사람의 눈이 60프레임이라고 할때, 우리가 눈을 깜빡이는 0.16초 이후 0.01초 사이에 총알 지나가면 총알 못봄
//우리는 그걸 그리는 중간 중간에 봐서 깜빡이는 것 처럼 보임
//현재 1번 문제.
//그래서 우리는 픽셀 데이터(도화지)를 2개를 관리할 것임. 완성된 그림만을 보여주기 위함.
//현재 프로그램 2번 문제.
//또 메시지가 존재하지 않으면 프로그램이 돌지 않는다는 문제도 존재함. 인공지능이나 AI가 움직이는 건 어떻게 하려고?
//계속 계속 돌아가게 해줘야함.
// 우회 방법 -> 일정 시간마다 일부로 메시지를 발생시키는 방법이 있긴 한데
// 정석 방법 -> 프로그램의 구조자체를 변경하는 것.
for (auto& info : g_vecInfo) {
Rectangle(hdc,
info.g_ptObjectPos.x - info.g_ptObjScale.x / 2,
info.g_ptObjectPos.y - info.g_ptObjScale.y / 2,
info.g_ptObjectPos.x + info.g_ptObjScale.x / 2,
info.g_ptObjectPos.y + info.g_ptObjScale.y / 2);
}
//빨간 펜 다 썼으니, 기본 펜으로 다시 바꿔줌.
SelectObject(hdc, hDefaultPen);
SelectObject(hdc, hDefaultBrush);
//그리고 빨간 펜을 지워준다.
DeleteObject(hRedPen);
DeleteObject(hBlueBrush);
//그리기 종료.
EndPaint(hWnd, &ps);
}
break;
case WM_LBUTTONDOWN:
{
//마우스 왼쪽 누르기.
//4바이트 param인데, 2바이트 | 2바이트 이렇게 나눠서 비트마스킹... 비슷하게 해서 마우스 좌표를 보냄.
//코드보면 비트연산(<<, >>)으로 밀어서 가져옴.
//마우스의 x좌표, y좌표.
g_ptLT.x = LOWORD(lParam);
g_ptLT.y = HIWORD(lParam);
bLbtnDown = true;
}
break;
case WM_MOUSEMOVE:
g_ptRB.x = LOWORD(lParam);
g_ptRB.y = HIWORD(lParam);
//InvalidateRect(hWnd, nullptr, true);
break;
case WM_LBUTTONUP:
{
tObjInfo info = {};
info.g_ptObjectPos.x = (g_ptLT.x + g_ptRB.x) / 2;
info.g_ptObjectPos.y = (g_ptLT.y + g_ptRB.y) / 2;
info.g_ptObjScale.x = abs(g_ptRB.x - g_ptLT.x);
info.g_ptObjScale.y = abs(g_ptRB.y - g_ptLT.y);
g_vecInfo.push_back(info);
bLbtnDown = false;
}
break;
case WM_TIMER:
{
int a = 0;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
{
//키가 눌리는 걸 감지하는 것.
//무슨 키냐, 라는 걸 알아야 하지 않나?
switch (wParam) {
case VK_UP:
//위쪽 화살표.
//g_ptObjectPos.y -= 10;
//누를때마다 화면 갱신을 해줘야지.
// 이렇게 하면, 기존에 그려진 부분은 그대로 남아있슴... 그래서 기존에 있던 걸 지워줘야함.
//InvalidateRect(hWnd, nullptr, false);
//true로 바꾸면, 기존에 그려져 있던 픽셀들 전부 깔끔하게 청소함.
InvalidateRect(hWnd, nullptr, true);
break;
case VK_DOWN:
//g_ptObjectPos.y += 10;
InvalidateRect(hWnd, nullptr, true);
break;
case VK_LEFT:
//g_ptObjectPos.x -= 10;
InvalidateRect(hWnd, nullptr, true);
break;
case VK_RIGHT:
//g_ptObjectPos.x += 10;
InvalidateRect(hWnd, nullptr, true);
break;
//꾹 누르고 있으면 한템포 느림...
//KEYDOWN은 한 번 눌리고, 어느정도 딜레이 이후에 계속 들어오는 방식이라 그럼.
case 'W':
{
int a = 0;
}
//일반 키. 대문자 기준으로만 됨.
break;
}
}
break;
default: //따로 정의되지 않은 수많은 단축키들.
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// 정보 대화 상자의 메시지 처리기입니다.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
- 엔진의 코어는 단 하나만 있어야 한다. 한 번 생성된 이후에 다른 곳에서 생성되지 않도록, 오직 하나만이 존재해야 하는 디자인 패턴을 Singleton패턴이라고 한다.
- 이 패턴은 Manager등에서 많이 쓰인다.
댓글 남기기