hyrule 2022. 5. 16. 01:24

https://hyrule.tistory.com/111 

 

*** 공부 방법 ***

1. 코딩을 해야 하는 부분은 첫 부분에 변수나 함수, 메소드에 대한 선언이 코드블럭으로 표시되어 있다. //ex) MakeFunction(); 2. 코드블럭 하단에는 해당 선언에 대한 구현 로직이 작성되어 있다. (ex)

hyrule.tistory.com

 

 


 

CGameManager::m_hDC

Device Context 핸들을 저장할 변수를 선언한다.

이 핸들은 쉽게 말해 '도화지와 그리기 도구 역할을 수행한다.

 

GetDC()

인자로 윈도우 핸들(HWND)이 들어간다.

해당 윈도우에서 사용할 수 있는 Device Context 핸들을 반환한다.

초기화 시에 위의 변수에 저장해놓자.

 

ReleaseDC()

1번 인자: 윈도우 핸들(HWND)

2번 인자: Device Context 핸들(HDC)

아까 받아온 DC를 제거하기 위해선 이 함수를 사용해야 한다.

프로그램이 종료될 때 사용.

 

* HDC를 받아왔다면 이제 화면에 출력할 준비는 끝났다.

 

 

 


CGameManager::Render(float DeltaTime)

이제 Render 함수에 하나하나 추가를 해보자.

 

 

TextOut()

TextOut: 현재 프로젝트의 설정에 따라 두 함수 중에서 자동 선택해주는 함수이다.
        #ifdef UNICODE
        #define TextOut  TextOutW -> W = 유니코드
        #else
        #define TextOut  TextOutA -> A = 멀티바이트

 

1번 인자: Device Context 핸들

2, 3번 인자: 출력할 위치 

4번 인자: 출력할 내용

  • 4번 인자에는 TEXT(" ")를 사용하면 편리하다. 
    • 이것도 마찬가지로 유니코드와 멀티바이트 타입에 따라 자동으로 문자열의 타입을 정해준다.

5번 인자: 4번에서 출력할 내용의 길이

  • lstrlen(string)을 사용하면 된다.
    • 문자 집합에 맞추어 문자열의 길이를 구해주는 함수

    

Rectangle()

내부는 흰색, 테두리는 검은색인 사각형을 그린다.

1번 인자에는 HDC가 들어가고,

이후 4개의 인자는 RECT 구조체 순서대로 입력을 하면 된다.

 

 

Ellipse()

지정한 사이즈에 사각형 안에 들어가는 원을 그린다.

Rectangle()과 같은 인자를 받는다.
    

 

SetPixel()

1번 인자: HDC

2, 3번 인자: 좌표

4번 인자: RGB값

  • RGB(x, y, z)

MoveToEx()

MoveToEx: 선을 그리기 위한 시작점을 지정한다.

 

1번 인자: HDC

2, 3번 인자: 시작 지점 좌표

4번 인자: 이전 좌표를 돌려받을 포인터 주소 - 필요없으면 nullptr
  

 

LineTo()

위의 MoveToEx() 함수에 입력된 좌표를 시작점으로 LineTo() 함수에 입력된 좌표까지 선을 그린다

MoveToEx() 함수의 1~3번에 들어간 인자와 동일한 인자가 들어간다.


 

* 해보기

1. 위의 함수들을 사용해서 창에 그려 보기

2. Render() 함수가 한 번 실행될 때마다 1픽셀 씩 위아래로 움직이는 사각형을 그려 보기.

창의 끝에 부딫히면 반대 방향으로 이동한다.

구현에 성공했는지만 확인하고 다시 지울 내용이므로,

편의를 위해 움직이는 사각형과 관련된 변수들은 Render() 함수 안에 static 변수로 선언해서 모아놓자.

 

▼이런 식으로

 

 

 

<참고용 코드>

변경사항이 GameManager 클래스 뿐이므로 해당 클래스의 코드만 코드블록에 작성함.

//Class CGameManager
//GameManager.h

#pragma once

#include "GameInfo.h"
#include "Singleton.h"

class CGameManager
{

private:
	HINSTANCE m_hInst;
	HWND m_hWnd;
	HDC m_hDC;


	//static 메소드인 WinProc은 같은 static 변수만 처리가능하므로
	static bool m_Loop;


public:
	bool Init(HINSTANCE hInstance);
	int Run();

private:
	void Logic();
	void Input(float DeltaTime);
	void Update(float DeltaTime);
	void Collision(float DeltaTime);
	void Render(float DeltaTime);

	void Register();
	bool Create();

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

	DECLARE_SINGLETON(CGameManager)
};

 

 

//Class CGameManager
//GameManager.cpp

#include "GameManager.h"

//아이콘
#include "resource.h"


DEFINITION_SINGLETON(CGameManager)
bool CGameManager::m_Loop = true;

CGameManager::CGameManager()
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    //_CrtSetBreakAlloc(141);
}
CGameManager::~CGameManager()
{
}

bool CGameManager::Init(HINSTANCE hInstance)
{
	m_hInst = hInstance;

	// 윈도우클래스 구조체를 만들어주고 등록한다.
	Register();

	// 윈도우 창을 생성하고 보여준다.
	Create();

    m_hDC = GetDC(m_hWnd);

	return true;
}

int CGameManager::Run()
{
    MSG msg;

    while (m_Loop)
    {
        if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else 
        {
            Logic();
        }
    }

    return (int)msg.wParam;
}

void CGameManager::Logic()
{
    Input(0.0f);
    Update(0.0f);
    Collision(0.0f);
    Render(0.0f);
}

void CGameManager::Input(float DeltaTime)
{
}

void CGameManager::Update(float DeltaTime)
{
}

void CGameManager::Collision(float DeltaTime)
{
}

void CGameManager::Render(float DeltaTime)
{
    // TextOutA : 멀티바이트 문자열(char 문자열)을 출력하는 함수이다.
    // TextOutW : 유니코드 문자열(wchar_t 문자열)을 출력하는 함수이다.
    // TextOut : 현재 프로젝트의 설정이 멀티바이트냐 유니코드냐에 따라 위의 두 함수중 하나가 결정된다.
    TextOut(m_hDC, 50, 50, TEXT("텍스트 출력"), lstrlen(TEXT("텍스트 출력")));
    TextOut(m_hDC, 500, 50, TEXT("또 출력"), lstrlen(TEXT("또 출력")));

    Rectangle(m_hDC, 100, 100, 200, 200);
    Ellipse(m_hDC, 200, 100, 300, 200);

    for (int i = 0; i < 10; ++i)
    {
        SetPixel(m_hDC, 350 + i, 100, RGB(255, 0, 0));
    }

    // MoveToEx : 선을 그리기 위해서 시작점을 지정한다.
    MoveToEx(m_hDC, 100, 300, nullptr);

    // LineTo : 마지막에 지정된 점으로부터 현재 점을 연결하는 선을 그려낸다.
    LineTo(m_hDC, 150, 350);

    LineTo(m_hDC, 200, 350);

    MoveToEx(m_hDC, 450, 300, nullptr);

    LineTo(m_hDC, 500, 300);

    static RECT m_TestRC = { 1000, 100, 1100, 200 };
    static float Top = 100.f;
    static float Bottom = 200.f;
    static int m_Dir = 1;



    m_TestRC.top += m_Dir;
    m_TestRC.bottom += m_Dir;

    if (m_TestRC.bottom >= 720)
    {
        m_Dir = -1;
    }

    else if (m_TestRC.top <= 0)
    {
        m_Dir = 1;
    }

    Rectangle(m_hDC, m_TestRC.left, m_TestRC.top, m_TestRC.right, m_TestRC.bottom);


}



void CGameManager::Register()
{
    // 레지스터에 등록할 윈도우 클래스 구조체를 만들어준다.
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;

    // 메세지큐에서 꺼내온 메세지를 인자로 전달하며 호출할 함수의 함수 주소를
    // 등록한다.
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;

    // 윈도우 인스턴스를 등록한다.
    wcex.hInstance = m_hInst;

    // 실행파일에 사용할 아이콘을 등록한다.
    wcex.hIcon = LoadIcon(m_hInst, MAKEINTRESOURCE(IDI_ICON1));

    // 마우스 커서 모양을 결정한다.
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

    // 메뉴를 사용할지 말지를 결정한다.
    wcex.lpszMenuName = nullptr;

    // 등록할 클래스의 이름을 유니코드 문자열로 만들어서 지정한다.
    // TEXT 매크로는 프로젝트 설정이 유니코드로 되어있을 경우 유니코드 문자열로 만들어지고
    // 멀티바이트로 되어있을 경우 멀티바이트 문자열로 만들어지게 된다.
    wcex.lpszClassName = TEXT("GameFramework");

    // 윈도우창 좌상단에 표시할 작은 아이콘을 등록한다.
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_ICON1));

    RegisterClassExW(&wcex);
}

bool CGameManager::Create()
{
    m_hWnd = CreateWindowW(TEXT("GameFramework"),
        TEXT("GameFramework"), WS_OVERLAPPEDWINDOW,
        100, 50, 1280, 720, nullptr, nullptr, m_hInst, nullptr);

    if (!m_hWnd)
    {
        return false;
    }

    RECT rc = { 0, 0, 1280, 720 };

    AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, false);

    MoveWindow(m_hWnd, 100, 50, abs(rc.left) + abs(rc.right), abs(rc.top) + abs(rc.bottom), true);

    // 윈도우 창을 보여준다. 1번인자에 들어간 핸들의 윈도우 창을 보여줄지 말지를
    // 결정해준다.
    ShowWindow(m_hWnd, SW_SHOW);

    // 이 함수를 호출하여 클라이언트 영역이 제대로 갱신되었다면 0이 아닌 값을 반환하고
    // 갱신이 실패했을 경우 0을 반환한다.
    UpdateWindow(m_hWnd);

    
    return true;
}

LRESULT CGameManager::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_DESTROY:
        // 윈도우가 종료될때 들어오는 메세지이다.
        m_Loop = false;
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}