본문 바로가기
개발개발/WinAPI & DirectX

기본 구조 설계 : 게임관리자/게임정보/메인 클래스 제작, 폴더 정리를 위한 솔루션 제거와 기존 프로젝트를 솔루션에 추가

by 유잉유잉유잉 2025. 2. 3.
728x90

 

게임을 만들기에 앞서서, 기본 구조를 우리가 수정하기 좋은 구조로 설계하여 새로 만들어보겠습니다.

 

 

▶️새프로젝트 만들기

새 프로젝트 만들기 - Windows 데스크톱 마법사 를 이용하여 새 프로젝트를 생성합니다. 

 

프로젝트 이름을 설정하고

만들기를 누르면

어플리케이션 종류를 설정할 수 있는 창이 출력됩니다.

  • 콘솔 애플리케이션 - 기존에 했던 콘솔창으로 출력 
  • 데스크톱 애플리케이션 - 우리가 사용할 애플리케이션 종류.
  • dll - 동적링크 라이브러리(dynamic Link Library). 
    • 동적으로 코드를 링크를 걸고 사용할 수 해주게 해주는 라이브러리 종류
  • lib - 정적링크 라이브러리.
    • 링크를 걸고 라이브러리화 되어있는 코드를 우리코드로 끌어다가 쓸 때 사용  

 

빈프로젝트 체크 - 텅 비어있는 상태로 프로젝트를 생성합니다. 

 

 

main.cpp 파일을 생성해줍니다.

 

 

▶️ 솔루션 삭제와 솔루션 추가

그리고

프로젝트 폴더의 정리를 위해서 

프로젝터를 솔루션 탐색기에서 제거합니다.

프로젝트 파일들이 실 제로 삭제되진 않고, 솔루션 탐색기에서만 보이지 않으니 걱정하지 마세요

탐색기로 프로젝트가 있는 폴더에 들어가서 Include라는 폴더를 만들고 모든 파일을 Include폴더안으로 옮겨줍니다.

그리고 Bin폴더와 BinObj폴더를 새로 생성해줍니다.

변경전 변경후

 

컴파일을 하게 되면

중간 컴파일이 되어 생성되는 오브젝트 파일 등을 binObj에 생성되게 할 것이고

최종 실행파일의 생성은 Bin폴더에 생성되게 할 것입니다.

 

 

이제 다시 솔루션을 솔루션 탐색기에 추가해보겠습니다.

프로젝트 폴더에서 include 폴더로 이동되어 있는 vcxproj 파일을 선택하여 열어줍니다.

기존과 동일하게 프로젝트가 다시 열렸고, main.cpp도 잘 열리는것을 확인하실 수 있습니다.

 

 

▶️ obj파일과 exe파일 생성 디렉터리 설정

다시 솔루션을 선택하고 속성을 들어가봅시다

 

속성 페이지에서 상단의 구성과 플랫폼을

'모든구성'과 '모든 플랫폼'으로 변경해줍니다.

 

중간 디렉터리를 

현재 프로젝트 파일이 있는 이전 폴더의 BinObj로 설정해주기 위해 '../BinObj/'  로 설정하고

출력 디렉터리를 '../Bin/'로 설정해줍니다.

'적용'을 눌러서 적용시켜줍니다. 

 

이렇게 설정하면 중간에 생성되는 컴파일 파일들은 BinObj폴더에,

최종 실행파일은 Bin폴더에 저장됩니다.

 

 

▶️ 디버그모드일때 실행파일 이름을 변경해봅시다

  1. 구성에서 'Debug'를 설정하고
  2. 대상이름 뒤에 프로젝트이름 뒤에 '_Debug'를 붙여주고 적용, 또는 확인을 누르고 창을 닫습니다.

 

이렇게 설정하면 디버그모드 일 땐,  '프로젝트이름_Debug.exe' 라는 이름으로 실행파일이 만들어지기 때문에 

릴리즈 모드(상용모드)의 실행파일과 구분이 됩니다.

 

 

 

▶️ wWinMain() 함수를 main.cpp에 작성해봅시다.

#include <Windows.h>

int APIENTRY wWinMain ( _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow )
{
    return 0;
}

 

위 코드를 실행하면 

BinObj 폴더 안 Bin 폴더 안 Include 폴더 안

폴더별로 파일이 정리되는 것을 확인하실 수 있습니다. 

 

기본 파일관련 세팅은 완료되었습니다.

 

 

▶️ 게임 관리자 클래스를 만들어봅시다

1) 게임 관리자용 필터를 생성해줍니다

(1)솔루션 선택 - 추가 - 새필터 - 01. GameManager 추가 

 

(2) GameManager 클래스를 생성합니다

 

만들어진 클래스와 헤더파일은 드래그 혹은 잘라내기/붙여넣기로 해당 필터로 옮겨줍니다.

 

(3) 공용 게임 정보를 관리할 GameInfo.h 헤더파일을 만들어줍니다.

main.cpp의 include한 windows.h는 gameInfo.h로 옮깁니다.

 

 

 

 

▶️ 아이콘 파일 만들기

솔루션 탐색기에서 '리소스 파일' 폴더 선택 - 추가 - 리소스  

리소스 추가 창에서 - ICON 선택 - 새로만들기

생성하면 .rc파일과 resource.h파일이 생성됩니다.

resource.h파일을 열면 

이렇게 등록된 리소스가 id로 관리되는 것을 확인하실 수 있습니다.

등록된 아이콘이 IDI_ICON1로 등록되었고,

해당 값을 실행파일 아이콘을 지정하는 LoadIcon함수에 넣어주면 됩니다.

 

 

▶️GameInfo.h

#pragma once

#include <Windows.h>

// 삭제 매크로
#define SAFE_DELETE(p) if(p) { delete p; p = nullptr; }

// 싱글톤 선언 매크로
#define DECLARE_SINGLE(Type)\
private : \
	Type();\
	~Type();\
private:\
	static Type* mInst;\
public:\
	static Type* GetInst()\
	{\
		if( !mInst )\
			mInst = new Type;\
		return mInst;\
	}\
	static void DestroyInst()\
	{\
		SAFE_DELETE(mInst);\
	}

// 싱글톤 변수 초기화 매크로
#define DEFINITION_SINGLE(Type) Type* Type::mInst = nullptr;

 

▶️ main.cpp

#include "GameManager.h"

int APIENTRY wWinMain ( _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow )
{
    if ( !CGameManager::GetInst ()->Init ( hInstance ) )
    {
        CGameManager::DestroyInst ();
        return 0;
    }

    int Ret = CGameManager::GetInst ()->Run ();

    CGameManager::DestroyInst ();

    return Ret;
}

 

 

▶️ GameManager.h

#pragma once

#include "GameInfo.h"

class CGameManager
{
private :
	static bool	mLoop;
	// 윈도우 HINSTANCE 저장.
	HINSTANCE	mhInst		= 0;
	HWND		mWnd		= 0;
	// 윈도우 클래스 이름 저장.
	TCHAR		mClassName[256] = {};
	TCHAR		mTitleName[256] = {};

public :
	// 윈도우창 만들 때 HINSTANCE가 필요. wWinMain에서 인자로 받아서 사용한다
	bool Init ( HINSTANCE hInst );

	// wWinMain() 반환타입을 int타입으로 받기 때문에 
	// 윈도우창이 종료되고 반환할 값을 동일하게 반환하는 Run() 함수를 만들어준다.
	int Run ();

private:
	void RegisterWindowClass ();
	bool Create ();

	static LRESULT CALLBACK    WndProc ( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );

	DECLARE_SINGLE ( CGameManager )
};

 

 

▶️GameManager.cpp

#include "GameManager.h"
#include "resource.h"

DEFINITION_SINGLE ( CGameManager )

bool CGameManager::mLoop = true;

CGameManager::CGameManager () 
{
}
CGameManager::~CGameManager () 
{
}

bool CGameManager::Init ( HINSTANCE hInst )
{
	mhInst = hInst;

	// lstrcpy() : 유니코드 문자열 복사함수
	// Text()를 사용해도 되고 L""로 문자열을 넣어도 된다( 동일함 )
	lstrcpy ( mClassName, TEXT( "MyFramework" ) );
	lstrcpy ( mTitleName, TEXT( "MyFramework" ) );

    RegisterWindowClass ();

    if ( !Create () )
        return false;

	return true;
}

int CGameManager::Run ()
{
    MSG msg;

    while ( mLoop )
    {
        if ( PeekMessage ( &msg, nullptr, 0, 0, PM_REMOVE ) )
        {
            // 키보드 입력 메세지가 발생할 경우 동작한다.
            // WM_KEYDOWN, WM_KEYUP 등 메세지가 발생하면 문자일 경우 WM_CHAR 메세지를 하나더
            // 만들어주는 역할을 한다.
            TranslateMessage ( &msg );

            // 메세지를 WndProc로 전달해준다.
            DispatchMessage ( &msg );
        }

        // 윈도우 데드타임일 경우 동작한다.(메세지 큐에 메세지가 없는 경우)
        else
        {
            
        }
    }

    return ( int ) msg.wParam;
}

void CGameManager::RegisterWindowClass ()
{
    WNDCLASSEXW wcex;

    // 윈도우클래스 구조체의 크기를 나타낸다. 반드시 지정되어야 한다.
    wcex.cbSize = sizeof ( WNDCLASSEX );

    // 화면에 출력가능한 영역을 클라이언트 영역이라고 한다.
    // 클라이언트 영역의 크기(가로, 세로)가 변경될 시 전부 다시 그려주도록 한다.
    wcex.style = CS_HREDRAW | CS_VREDRAW;

    // 메세지큐에서 얻어온 메세지를 인자로 넣어서 호출해줄 함수의 주소를 넘겨준다.
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;

    // 운영체제에서 부여해준 HINSTANCE를 전달한다.
    wcex.hInstance = mhInst;

    // 실행파일 아이콘을 지정한다.
    // MAKEINTRESOURCE() : 인자로 들어간 리소스 아이디를 이용해서 리소스를 로드하는 함수.
    // #include "resource.h" 필요
    wcex.hIcon = LoadIcon ( mhInst, MAKEINTRESOURCE ( IDI_ICON1 ) );

    // 윈도우 창에서의 커서 모양을 나타낸다.
    wcex.hCursor = LoadCursor ( nullptr, IDC_ARROW );

    // 클라이언트 영역의 색상을 지정한다.
    wcex.hbrBackground = ( HBRUSH ) ( COLOR_WINDOW + 1 );

    // 윈도우 메뉴를 지정한다.
    // 0을 대입하면 메뉴를 없앤다.
    wcex.lpszMenuName = 0;

    // 등록할 윈도우클래스의 이름을 지정한다.
    wcex.lpszClassName = mClassName;

    // 윈도우창 좌측 상단의 작은 아이콘을 지정한다.
    wcex.hIconSm = LoadIcon ( wcex.hInstance, MAKEINTRESOURCE ( IDI_ICON1 ) );

    // 위에서 설정한 윈도우클래스를 등록한다.
    RegisterClassExW ( &wcex );
}

bool CGameManager::Create ()
{
    // CreateWindow : 윈도우 창을 생성해주는 함수이다.
    // WinAPI에서 함수명뒤에 W가 붙으면 유니코드, A가 붙으면 멀티바이트이다.
    // 1번인자 : 윈도우 클래스 이름을 지정한다.
    // 2번인자 : 윈도우 타이틀바에 출력할 이름을 지정한다.
    // 3번인자 : 윈도우 창의 모양을 결정한다.
    // 4번인자 : 화면에서 윈도우가 시작할 X지점을 지정한다.
    // 5번인자 : 화면에서 윈도우가 시작할 Y지점을 지정한다.
    // 6번인자 : 윈도우 창의 가로 크기를 지정한다.
    // 7번인자 : 윈도우 창의 세로 크기를 지정한다.
    // 8번인자 : 부모윈도우가 있을 경우 부모윈도우의 핸들을 지정한다.
    // 9번인자 : 메뉴 핸들을 전달한다.
    // 10번인자 : 윈도우 인스턴스를 전달한다. WinMain에서 전달은 값으로 전달해야 한다.
    // 11번인자 : 창 생성 데이터를 지정한다. WM_CREATE는 윈도우 생성시 발생하는 메세지인데
    // 이 메세지가 발생하면 WndProc 함수의 lParam에 이 값이 전달된다.
    // 이렇게 윈도우를 생성하면 윈도우 핸들을 만들어준다.
    // 잘못된 생성일 경우 0을 반환한다.
    mWnd = CreateWindowW ( mClassName, mTitleName, WS_OVERLAPPEDWINDOW,
        //CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, mhInst, nullptr );
        CW_USEDEFAULT, 0, 1280, 720, nullptr, nullptr, mhInst, nullptr );

    if ( !mWnd )
    {
        return false;
    }

    // 위에서 윈도우 창을 만들었다면 ShowWindow 함수를 이용해서 창을 보여줄지 숨길지를
    // 결정한다.
    ShowWindow ( mWnd, SW_SHOW );

    // 클라이언트 영역을 강제로 다시 그리게 요청해주는 함수이다.
    // 처음 생성시나 특정상황에 창을 새로고침 해야 할 경우 사용한다.
    UpdateWindow ( mWnd );

    return true;
}

LRESULT CGameManager::WndProc ( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    switch ( message )
    {
        // 윈도우 창 종료메세지
    case WM_DESTROY:
        mLoop = false;
        PostQuitMessage ( 0 );
        break;
    default:
        // 위에서 지정한 메세지 외의 다른 메세지가 발생할 경우 윈도우의 기본 동작으로
        // 처리가 되게 만들어준다.
        return DefWindowProc ( hWnd, message, wParam, lParam );
    }
    return 0;
}

 

 

 

정상적으로 프로젝트가 생성 및 작성 되었다면

빌드시 아래와 같이 빈 윈도우 창이 출력됩니다.

 

 

게임 작성전 빈 윈도우창을 출력하기 위한 기본 구조를 작성해보았습니다.

 

728x90

댓글