[WinApi] Engine 만들기 시작, string_view

라멘커비·2024년 1월 23일
0

WinApi

목록 보기
2/32

윈도우가 우리한테 주는 권한들

HINSTANCE : 프로그램 핸들 (내 프로그램은 4800번)
HWND : 윈도우 핸들 (4800번에 530번 윈도우 크기를 바꾸고 싶다)
HDC : 윈도우에 그릴 수 있는 권한 (4800번에 530번 윈도우에서 230번 그림 권한으로 사각형 그리고 싶다)
HMENU
HCURSOR
HICON
. . .

  • 대부분의 함수에서 인자로 0을 넣으면 디폴트로 해주는 경우가 많다.

DefWindowProc : 일반적인 사용자가 정의할 수 없는 메시지는 나에게 넘기면 윈도우가 기본적으로 처리하는 방식으로 처리해 주겠다.

GetMessage : 메시지가 없으면 그 자리에서 프로그램을 정지시키고 메시지가 올 때까지 대기한다. (_getch() 함수처럼 => 입력버퍼에 입력이 있을 때까지 그자리에서 기다린다.)
PeekMessage : 메시지가 없으면 그냥 리턴.
PeekMessage 마지막 인자 PM_REMOVE : 쌓여있는 메시지 다 지워버린다.
PM_NOREMOVE : 윈도우 메시지가 계속 쌓인다. 처리가 안 되서 렉걸림. 윈도우가 정지한 것처럼 보일 것이다.

Engine 시작

엔진 시작 코드

🪀EngineBase (Level0 - 낮을수록 근본)

빈 프로젝트로 EngineBase 새로 만듦. 안에 EngineDebug있음. 정적라이브러리 속성 설정. C++ 20 표준으로.
기본이 되는 디버깅 기능과 스트링 기능을 위한 클래스 및 파일들.
window 프로그래밍을 할 때 언제 어디서나 쓰일 코드들을 넣을 것이다.

🪀EnginePlatform (Level1)

빈 프로젝트 새 프로젝트, 내부에 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(Level2)

안에 EngineCore클래스 있음. 정적라이브러리 속성 설정.
게임 엔진들이 처리해야 할 내용들을 올릴 것이다. 오브젝트 구조, 삭제 구조, 렌더링 구조 등등.

Level0~2는 선생님과 완전히 동일해야 함. 3부터는 각자의 영역.

🪀컨텐츠(Level3)

심혈을 기울여서,,, 여기에 무슨 코드를 쳤는지 단 1개도 몰라서는 안 된다.
Player같은 애들이 여기 있어야 함. Engine을 다 참조.

🪀App(Level4)

얘는 구성 속성 > 일반 > 앱 구성 형식 애플리케이션(.exe)임.
속성 > 링커 > 하위 시스템 : 창으로 바꿔야 함.

App에 winMain 만들고 한 번 빌드하면 모든 프로젝트의 파일에 각각 중간파일들이 생김. 다 한 군데로 나오게 만들 것이다. 중간파일 다 지우고싶으면 한 번에 지울 수 있도록.
출력 디렉토리, 중간 디렉토리 설정.

출력 디렉토리 $(SolutionDir)Bin$(Platform)$(Configuration)\
중간 디렉토리 $(SolutionDir)Bin$(Platform)$(Configuration)$(ProjectName)\

🪀지금까지 엔진 코드 흐름(ENGINESTART)

  • App에서 시작은 ENGINESTART로 한다.
#include <Windows.h>
#include <EngineCore\EngineCore.h>
#include <EnginePlatform\EngineWindow.h>
#include <Kirby\KirbyCore.h>

ENGINESTART(KirbyCore)
  • ENGINESTART는 Level2인 EngineCore에 define되어 있고, wWinMain함수를 숨긴 것이다. USERCORE로 KirbyCore를 넣어줬으니 NewUserCore는 KirbyCore이다.
    이걸 업캐스팅한게 Ptr포인터이다.
#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(); \
}
  • Ptr포인터로 CoreInit함수를 실행해서 EngineWindow의 Init을 실행해서 HINSTANCE를 받아오고, 윈도우를 연다.
void EngineCore::CoreInit(HINSTANCE _HINSTANCE)
{
	if (true == EngineInit)
	{
		return;
	}

	EngineWindow::Init(_HINSTANCE);
	MainWindow.Open();

	EngineInit = true;
}

string_view

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::stringchar* 호환
    const std::string& ← 얘는 안에 std::vector 들고있는 것과 같음

Open함수의 인자 Title을 const char* 로 받았을 경우, CreateWindow의 인자로 들어갈 때 const std::string&로 바꿔줘야 한다.

&_Title[0] 로 표현하거나
_Title.c_str() 함수 사용 (←내부에서 바꿔줌)

하지만 얕은 복사로 값을 옮겨주는 string_view로 했당..~

근데 string에서도 SSO(Small/Short String Optimization)가 있기 때문에 25글자 이하는 동적할당을 하지 않고 받는다.

속성 바꿈) 경고를 오류로 처리하기로 함.
전부 다 C++ 20, 멀티바이트로 하기로 함.

profile
일단 시작해보자

0개의 댓글