HINSTANCE : 프로그램 핸들 (내 프로그램은 4800번)
HWND : 윈도우 핸들 (4800번에 530번 윈도우 크기를 바꾸고 싶다)
HDC : 윈도우에 그릴 수 있는 권한 (4800번에 530번 윈도우에서 230번 그림 권한으로 사각형 그리고 싶다)
HMENU
HCURSOR
HICON
. . .
DefWindowProc
: 일반적인 사용자가 정의할 수 없는 메시지는 나에게 넘기면 윈도우가 기본적으로 처리하는 방식으로 처리해 주겠다.
GetMessage
: 메시지가 없으면 그 자리에서 프로그램을 정지시키고 메시지가 올 때까지 대기한다. (_getch()
함수처럼 => 입력버퍼에 입력이 있을 때까지 그자리에서 기다린다.)
PeekMessage
: 메시지가 없으면 그냥 리턴.
PeekMessage 마지막 인자 PM_REMOVE
: 쌓여있는 메시지 다 지워버린다.
PM_NOREMOVE
: 윈도우 메시지가 계속 쌓인다. 처리가 안 되서 렉걸림. 윈도우가 정지한 것처럼 보일 것이다.
빈 프로젝트로 EngineBase 새로 만듦. 안에 EngineDebug있음. 정적라이브러리 속성 설정. C++ 20 표준으로.
기본이 되는 디버깅 기능과 스트링 기능을 위한 클래스 및 파일들.
window 프로그래밍을 할 때 언제 어디서나 쓰일 코드들을 넣을 것이다.
빈 프로젝트 새 프로젝트, 내부에 EngineWindow 클래스 만듦. 정적라이브러리 속성 설정.
Window에서 사용하는 특수한 윈도우나 사운드 등. 운영체제의 영향을 크게 받는 클래스들을 넣을 것이다. EngineBase를 사용할 것이다.
EnginePlatform 프로젝트에 있는 EngineWindow에서는 윈도우창을 만들고 띄우는 Open함수와 메시지 루프를 돌리는 WindowMessageLoop함수가 있다.
#include "EngineWindow.h"
#include <EngineBase\EngineDebug.h>
bool EngineWindow::WindowLive = true;
HINSTANCE EngineWindow::hInstance;
LRESULT CALLBACK EngineWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
WindowLive = false;
// PostQuitMessage(123213);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
void EngineWindow::Init(HINSTANCE _hInst)
{
hInstance = _hInst;
}
EngineWindow::EngineWindow()
{
}
EngineWindow::~EngineWindow()
{
}
void EngineWindow::Open(std::string_view _Title /*= "Title"*/)
{
// 간혹가다가 앞쪽이이나 뒤쪽에 W가 붙거나 A가 붙어있는 함수들을 보게 될겁니다.
// A가 붙어있으면 멀티바이트 함수
// W가 붙어있으면 와이드 바이트 함수
WNDCLASSEXA wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = "DefaultWindow";
wcex.hIconSm = nullptr;
RegisterClassExA(&wcex);
// const std::string& = 내부에 뭘들고 있다고 생각하라고 했나요?
// std::vector<char> 들고 있다고 생각하라고 했다.
// _Title[0] = char&를 리턴해준 것과 같다.
// _Title.c_str(); => 자연스럽게 내부에서
// const char* Test = &_Title[0]
// return Test;
hWnd = CreateWindowA("DefaultWindow", _Title.data(), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
MsgBoxAssert("윈도우 생성에 실패했습니다.");
return;
}
hDC = GetDC(hWnd);
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
}
unsigned __int64 EngineWindow::WindowMessageLoop()
{
MSG msg = {};
while (WindowLive)
{
// 기본 메시지 루프입니다:
// 10개가 들어있을
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
안에 EngineCore클래스 있음. 정적라이브러리 속성 설정.
게임 엔진들이 처리해야 할 내용들을 올릴 것이다. 오브젝트 구조, 삭제 구조, 렌더링 구조 등등.
Level0~2는 선생님과 완전히 동일해야 함. 3부터는 각자의 영역.
심혈을 기울여서,,, 여기에 무슨 코드를 쳤는지 단 1개도 몰라서는 안 된다.
Player같은 애들이 여기 있어야 함. Engine을 다 참조.
얘는 구성 속성 > 일반 > 앱 구성 형식
애플리케이션(.exe)임.
속성 > 링커 > 하위 시스템
: 창으로 바꿔야 함.
App에 winMain 만들고 한 번 빌드하면 모든 프로젝트의 파일에 각각 중간파일들이 생김. 다 한 군데로 나오게 만들 것이다. 중간파일 다 지우고싶으면 한 번에 지울 수 있도록.
출력 디렉토리, 중간 디렉토리 설정.
출력 디렉토리 $(SolutionDir)Bin$(Platform)$(Configuration)\
중간 디렉토리 $(SolutionDir)Bin$(Platform)$(Configuration)$(ProjectName)\
#include <Windows.h>
#include <EngineCore\EngineCore.h>
#include <EnginePlatform\EngineWindow.h>
#include <Kirby\KirbyCore.h>
ENGINESTART(KirbyCore)
#define ENGINESTART(USERCORE) \
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, \
_In_opt_ HINSTANCE hPrevInstance, \
_In_ LPWSTR lpCmdLine, \
_In_ int nCmdShow) \
{ \
USERCORE NewUserCore; \
EngineCore* Ptr = &NewUserCore; \
Ptr->CoreInit(hInstance); \
Ptr->EngineStart(); \
EngineWindow::WindowMessageLoop(); \
}
void EngineCore::CoreInit(HINSTANCE _HINSTANCE)
{
if (true == EngineInit)
{
return;
}
EngineWindow::Init(_HINSTANCE);
MainWindow.Open();
EngineInit = true;
}
string으로 받으면 new가 일어나지만 string_view로 받으면 참조형으로 얕은 복사가 일어나기때문에 8바이트짜리만 복사가 일어난다.
void FunctionView(std::string_view Name){
}
// 이것도 비슷
void FunctionString(const std::string& Name){
}
string과 string_view는 내부에서 이런 차이가 있다.
class MyString
{
public:
// Small/Short String Optimization
static char SSO[25];
char* Test;
MyString(const char* _Ptr)
{
Test = new char[strlen(_Ptr)];
}
};
class MyString_View
{
public:
const char* Test;
size_t Size;
MyString_View(const char* _Ptr)
{
Test = _Ptr;
}
};
std::string
과 char*
호환Open함수의 인자 Title을 const char* 로 받았을 경우, CreateWindow의 인자로 들어갈 때 const std::string&로 바꿔줘야 한다.
&_Title[0] 로 표현하거나
_Title.c_str() 함수 사용 (←내부에서 바꿔줌)
하지만 얕은 복사로 값을 옮겨주는 string_view로 했당..~
근데 string에서도 SSO(Small/Short String Optimization)가 있기 때문에 25글자 이하는 동적할당을 하지 않고 받는다.
속성 바꿈) 경고를 오류로 처리하기로 함.
전부 다 C++ 20, 멀티바이트로 하기로 함.