4강 프로젝트 설정
프로젝트 만들기 & 설정하기
[1]
프로젝트 생성 - C++ 언어 설정 - Window 데스크탑 애플리케이션 프로젝트 만들기 - 정적 라이브러리 타입의 프로젝트 추가 (이름은 Engine)
- 필터 추가 (Engine, Resource, Utils) - 한글 폴더 제거 - Utils필터에 소스코드들 추가 - Engine.cpp 제거 (필요 없다)
DLL(동적 라이브러리)와 정적 라이브러리의 차이점
DLL : 런타임에 연결되는 라이브러리
정적 라이브러리 : 소스 코드를 빌드할때 포함되어 연결되는 라이브러리
라이브러리(엔진)을 분리하는 이유
클라이언트 (컨텐츠 부분) 과 엔진 부분을 나누기 위함이다
빌드 시 엮여있는 상황이 될 것. 예시로 엔진 수정 시 클라이언트까지 빌드를 해야 함 ⇒ 최악🤮
[2] Engine 프로젝트의 특징
메인 함수가 없다
기능 모음집 (=라이브러리) 이기 때문이다
다음을 pch.h (프리 컴파일된 헤더) 에 코딩하고 framework.h 제거
1
2
3
// framework.h의 유일한 한줄 코드입니다
#define WIN32_LEAN_AND_MEAN // 거의 사용되지 않는 내용을 Windows 헤더에서 제외합니다.
(결과) - pch.h
1
2
3
4
5
6
#ifndef PCH_H
#define PCH_H
#define WIN32_LEAN_AND_MEAN // 거의 사용되지 않는 내용을 Windows 헤더에서 제외합니다.
#endif //PCH_H
- Utils 에 클래스 추가 (EnginePch)
- EnginePch.h 에 다음 코딩 (앞으로 사용하게 될 라이브러리들)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#pragma once
// 각종 include
#include <windows.h>
#include <tchar.h>
#include <memory>
#include <string>
#include <vector>
#include <array>
#include <list>
#include <map>
using namespace std;
#include "d3dx12.h"
#include <d3d12.h>
#include <wrl.h>
#include <d3dcompiler.h>
#include <dxgi.h>
#include <DirectXMath.h>
#include <DirectXPackedVector.h>
#include <DirectXColors.h>
using namespace DirectX;
using namespace DirectX::PackedVector;
using namespace Microsoft::WRL;
// 각종 lib (라이브러리)
#pragma comment(lib, "d3d12")
#pragma comment(lib, "dxgi")
#pragma comment(lib, "dxguid")
#pragma comment(lib, "d3dcompiler")
// 각종 typedef 오른쪽의 형을 왼쪽으로 사용할 수 있게 함
using int8 = __int8;
using int16 = __int16;
using int32 = __int32;
using int64 = __int64;
using uint8 = unsigned __int8;
using uint16 = unsigned __int16;
using uint32 = unsigned __int32;
using uint64 = unsigned __int64;
using Vec2 = XMFLOAT2;
using Vec3 = XMFLOAT3;
using Vec4 = XMFLOAT4;
using Matrix = XMMATRIX;
void HelloEngine();
내용 중 #include "d3dx12.h"
이 파일은 없음! 마이크로소프트 d3dx12.h파일 깃허브
이 곳에서 직접 다운 받아서 프로젝트에 넣어야 함 !
- 마이크로 소프트에서 지원해주는 비 공식적 유틸리티 라이브러리
- 편리하게 이용할 수 있는 기능들이 모여있음
이제 Engine.cpp 안의 다음 HelloEngine()
을 클라이언트에서 부를 수 있게 할 것
1
2
3
4
5
6
7
#include "pch.h"
#include "EnginePch.h"
void HelloEngine()
{
}
- pch.h 에 다음 추가
1
#include "EnginePch.h"
- EnginePch.h은 공용 헤더로써, 나중에 클라이언트에서 EnginePch.h 을 사용하기 위해서 따로 분리를 하였다
빌드해보기
빌드를 하면 프로젝트 폴더의 x64 - Debug 폴더 안에 내용물이 만들어진다.
Engine.lib
파일- 후에 이 파일을 클라이언트에서 사용하게 된다
- Client.exe 파일은 실행할 수 있지만, Engine파일은 그렇지 않음. 차이가 있다
[2]
Client 설정
Client 프로젝트 (우클릭) 속성에서 다음과 같이 설정
미리 컴파일된 헤더 - 사용 설정, pch.h 를 미리 컴파일된 헤더 파일로 설정
플랫폼을 x64로 설정
Client (우클릭) 클래스 추가 - pch 클래스 추가
- Client 필터 추가 Game, Utils - Utils 에 모든 소스코드 넣기 -
헤더파일
폴더 삭제
생성한 pch.cpp 에서 속성 페이지 - c/c++ 설정 - 미리컴파일된 헤더 : 만들기 설정
앞으로 생성할 cpp 파일들에서 다음 헤더를 가장 상위에 추가 해야 함
- 안 하면 에러가 난다
1
#include "pch.h"
[3] pch.h 설명
1
2
3
4
5
#pragma once
#pragma comment(lib, "Engine.lib")
#include "EnginePch.h"
#include "EnginePch.h"
에서 에러가 나는 이유 🔥🔥🔥
- Engine 폴더와 Client 폴더의 경로가 다르기 때문 ⇒ 경로를 설정해 주어야 한다!
[4] 빌드 결과물이 (x64 폴더 말고) 다른 폴더에 만들어지게 할 때, 사전 세팅
원하는 프로젝트 ( 여기선 Engine 프로젝트) - 속성 - 구성 속성 - 일반 - 출력 디렉터리
다음과 같이 설정
$(SolutionDir)[폴더명]\
이렇게 하면 빌드 후 [폴더명] 에 Engine 프로젝트 빌드 결과가 저장이 된다
[5] 클라이언트에서 Engine 의 코드들을 사용하기
클라이언트에서 Engine 의 코드들을 사용하기
⇒ 엔진의 [1] 헤더 파일들의 경로와 [2] 라이브러리 파일의 경로 [3] 사용할 파일들의 경로를 알려주어야 함
[1] Client 속성 - C/C++ 일반 - 추가 포함 디렉터리
$(SolutionDir)Engine\
[2] Client 속성 - 링커 - 일반 - 추가 라이브러리 디렉터리
$(SolutionDir)Output\
- 위 [4]단계에서 빌드 결과물이 있는 폴더 명을 써야 함
[3] 사용할 파일들을 알려주기
pch.h 에 #include "EnginePch.h"
윗줄에 다음 코딩
#pragma comment(lib, “Engine.lib”)
- 전처리기 단계에서 빌드하기 전에
“Engine.lib”
파일을 사용할 것을 요청함
⇒ 이 단계까지 끝나면 [3] pch.h 설명에서 났던 에러가 사라진다! 🔥🔥🔥
클라이언트에서 Engine 의 함수 사용하기
- 기능 추가는 X
[1]
Game 폴더 - 추가 클래스 - Game 클래스 추가
여기서 만들었던 Game.cpp 파일에 다음과 같이 코딩 ⇒ 엔진에 있던 함수를 호출하게 만들어보자
🔽 Game.cpp
1
2
3
4
5
6
7
8
9
10
11
12
#include "pch.h"
#include "Game.h"
void Game::Init()
{
HelloEngine();
}
void Game::Update()
{
}
HelloEngine();
: Engine.cpp 에 있던 함수이다 ! ✨
[2]
Client.cpp
파일에서 wWinMain
메인 함수 안에서 다음과 같이 무한루프를 돌며 기능을 수행 중이다
(라고 까지만 대강 알고 있자)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
while (GetMessage(&msg, nullptr, 0, 0))
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// TODO
game->Update();
}
🔼 무한 루프문
Game
클래스 내용을 메인함수 wWinMain
에서 호출하기
⇒ 다음을 wWinMain
의 while 무한루프 위에 코딩
1
2
unique_ptr<Game> game = make_unique<Game>(); //Game 클래스 만들기
game->Init(); //초기화 시키기
다음을 wWinMain
의 while 무한루프 안에 코딩
1
game->Update(); //갱신하기
- Game 이 while 문이 도는 주기 (프레임) 만큼 갱신이 된다
[3]
1
2
3
4
while (GetMessage(&msg, nullptr, 0, 0)) {
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
...
}
다음은 마우스나 키보드의 입력 등 이벤트가 있을 시 GetMessage
로 깨어나서 while문을 돌겠다는 의미이다 …
⇒ 그런 이벤트 없이, 이벤트 메시지가 있건 없건 실행되어야 하기 때문에 다음과 같이 코딩한다
완성된 메인함수의 while 문
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
while (true)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// TODO
game->Update();
}
- 스마트 포인터 사용
1
unique_ptr<Game> game = make_unique<Game>();
파일, 도움말 메뉴 뜨지 않게 하기
여기까지 과정을 실행하면 다음과 같이 파일, 도움말 메뉴가 달린 Client 가 실행된다
이를 없애보자
- Client.cpp 에서
MyRegisterClass
부근의wcex.lpszMenuName
값을 바꿔준다
1
wcex.lpszMenuName = nullptr;
결과) 파일, 도움말 메뉴가 사라져 말끔해진 Client.exe 실행 결과