리소스 매니저

Jaemyeong Lee·2024년 12월 22일

리소스 매니저 코드와 주석 내용을 기반으로, 각 코드 줄과 주석 내용을 꼼꼼히 읽고 분석하여 자세히 설명하겠습니다. 긴 코드지만, 각 주요 기능과 관련된 내용에 대해 차근차근 설명하겠습니다.


ResourceBase 클래스

리소스 관리의 기본 클래스로, 모든 리소스 객체의 공통 부모가 됩니다.

#pragma once
class ResourceBase
{
public:
	ResourceBase();
	virtual ~ResourceBase();
};

코드 설명:

  • #pragma once: 헤더 파일이 여러 번 포함되는 것을 방지합니다.
  • class ResourceBase: 리소스 관리의 기본 클래스 선언.
    • 리소스 매니저가 관리할 모든 리소스 객체의 공통 인터페이스로 활용됩니다.
    • 가상 소멸자를 통해 상속받는 객체를 안전하게 삭제할 수 있습니다.
  • virtual ~ResourceBase();: 가상 소멸자는 다형성을 보장하며, 파생 클래스에서 동적으로 생성된 메모리를 적절히 해제할 수 있도록 합니다.
#include "pch.h"
#include "ResourceBase.h"

ResourceBase::ResourceBase()
{
}

ResourceBase::~ResourceBase()
{
}

코드 설명:

  • 생성자와 소멸자는 현재 아무 동작도 하지 않지만, 필요한 초기화 및 정리 작업을 위해 준비되어 있습니다.
  • 기본 생성자와 소멸자를 정의했으므로 ResourceBase 객체를 안전하게 생성하고 삭제할 수 있습니다.

LineMesh 클래스

리소스 매니저가 관리할 구체적인 리소스 중 하나로, 선(Line)의 정보와 기능을 제공합니다.

#pragma once
#include "ResourceBase.h"
class LineMesh : public ResourceBase
{
public:
	void Save(wstring path);
	void Load(wstring path);

	void Render(HDC hdc, Pos pos) const;
protected:
	vector<pair<POINT, POINT>> _lines;
};

코드 설명:

  • class LineMesh : public ResourceBase: ResourceBase를 상속받아 리소스 매니저에 의해 관리될 수 있도록 합니다.
  • void Save(wstring path): _lines 데이터를 지정된 경로에 저장합니다.
  • void Load(wstring path): 파일에서 _lines 데이터를 읽어옵니다.
  • void Render(HDC hdc, Pos pos) const: 화면에 _lines 데이터를 렌더링합니다.
  • vector<pair<POINT, POINT>> _lines: 선의 시작점과 끝점을 저장하는 벡터.
#include "pch.h"
#include "LineMesh.h"
#include <fstream>

void LineMesh::Save(wstring path)
{
	wofstream file;
	file.open(path);

	int32 minX = INT32_MAX;
	int32 maxX = INT32_MIN;
	int32 minY = INT32_MAX;
	int32 maxY = INT32_MIN;

	for (auto& line : _lines)
	{
		POINT from = line.first;
		POINT to = line.second;

		minX = min(min(minX, from.x), to.x);
		maxX = max(max(maxX, from.x), to.x);
		minY = min(min(minY, from.y), to.y);
		maxY = max(max(maxY, from.y), to.y);
	}

	int32 midX = (maxX + minX) / 2;
	int32 midY = (maxY + minY) / 2;

	file << static_cast<int32>(_lines.size()) << endl;

	for (auto& line : _lines)
	{
		POINT from = line.first;
		from.x -= midX;
		from.y -= midY;
		POINT to = line.second;
		to.x -= midX;
		to.y -= midY;
		wstring str = std::format(L"({0},{1})->({2},{3})", from.x, from.y, to.x, to.y);

		file << str << endl;
	}

	file.close();
}

코드 설명:

  1. 파일 열기:
    • wofstream을 통해 UTF-16 기반 텍스트 파일을 작성합니다.
    • file.open(path);로 파일을 엽니다.
  2. 범위 계산:
    • _lines의 모든 좌표에서 최소값(minX, minY)과 최대값(maxX, maxY)을 계산합니다.
    • 중간 좌표를 midX, midY로 계산하여 기준점을 중앙으로 정렬합니다.
  3. 데이터 저장:
    • 라인의 개수를 저장하고, _lines를 순회하며 좌표값을 중앙 기준으로 이동시켜 저장합니다.
  4. 파일 닫기:
    • file.close();로 파일을 닫아 자원을 해제합니다.

void LineMesh::Load(wstring path)
{
	wifstream file;
	file.open(path);

	int32 count;
	file >> count;

	_lines.clear();

	for (int32 i = 0; i < count; i++)
	{
		POINT pt1;
		POINT pt2;

		wstring str;
		file >> str;

		::swscanf_s(str.c_str(), L"(%d , %d)->(%d , %d)", &pt1.x, &pt1.y, &pt2.x, &pt2.y);

		_lines.push_back(make_pair(pt1, pt2));
	}
	file.close();
}

코드 설명:

  1. 파일 열기:
    • wifstream을 통해 UTF-16 기반 텍스트 파일을 읽어옵니다.
  2. 데이터 초기화:
    • _lines를 초기화하여 중복 데이터를 방지합니다.
  3. 데이터 읽기:
    • 라인의 개수를 읽고, 해당 개수만큼 반복하며 파일에서 좌표 데이터를 읽어 _lines에 추가합니다.
    • swscanf_s를 사용하여 형식을 분석합니다.
  4. 파일 닫기:
    • file.close();로 파일을 닫아 자원을 해제합니다.

void LineMesh::Render(HDC hdc, Pos pos) const
{
	for (auto& line : _lines)
	{
		POINT pt1 = line.first;
		POINT pt2 = line.second;

		Pos pos1;
		pos1.x = pos.x + (float)pt1.x;
		pos1.y = pos.y + (float)pt1.y;

		Pos pos2;
		pos2.x = pos.x + (float)pt2.x;
		pos2.y = pos.y + (float)pt2.y;

		Utils::DrawLine(hdc, pos1, pos2);
	}
}

코드 설명:

  1. 렌더링 루프:
    • _lines의 모든 라인을 순회하며 화면에 그립니다.
  2. 좌표 변환:
    • 각 선의 시작점과 끝점을 Pos 객체로 변환하고, 기준 좌표(pos)를 기준으로 변환합니다.
  3. 라인 그리기:
    • Utils::DrawLine을 호출하여 실제 화면에 선을 렌더링합니다.

아직 분석이 끝나지 않았습니다. 리소스 매니저와 Game.cpp 등 다른 파일에 대한 상세 설명을 계속 작성해드릴까요?

ResourceManager 클래스

리소스를 관리하는 핵심 클래스입니다. 리소스 객체를 생성, 검색, 삭제하는 기능을 제공합니다.

#pragma once

class ResourceBase;
class LineMesh;

class ResourceManager
{
public:
	DECLARE_SINGLE(ResourceManager)

	~ResourceManager();

public:
	void Init();
	void Clear();

	const LineMesh* GetLineMesh(wstring key);

private:
	unordered_map<wstring, LineMesh*> _lineMeshes;
};

코드 설명:

  1. 전방 선언:

    • ResourceBaseLineMesh를 전방 선언하여 헤더 파일의 의존성을 줄입니다.
    • 이로 인해 클래스의 정의 없이도 포인터 또는 참조를 사용하여 선언할 수 있습니다.
  2. 싱글톤 매크로:

    • DECLARE_SINGLE(ResourceManager)를 통해 싱글톤 패턴을 적용합니다.
    • ResourceManager는 프로그램 전역에서 하나의 인스턴스만 존재하도록 보장됩니다.
  3. 주요 메서드:

    • Init(): 리소스를 초기화합니다.
    • Clear(): 모든 리소스를 해제합니다.
    • GetLineMesh(wstring key): 키를 기준으로 LineMesh 객체를 검색합니다.
  4. 멤버 변수:

    • _lineMeshes: 키와 LineMesh 객체를 연결하는 맵으로, 리소스를 효율적으로 관리합니다.

#include "pch.h"
#include "ResourceManager.h"
#include "LineMesh.h"

ResourceManager::~ResourceManager()
{
	Clear();
}

코드 설명:

  • 소멸자에서 Clear()를 호출하여 모든 리소스를 안전하게 해제합니다.

void ResourceManager::Init()
{
	LineMesh* mesh = new LineMesh();
	mesh->Load(L"Player.txt");

	_lineMeshes[L"Player"] = mesh;
}

코드 설명:

  1. 리소스 초기화:
    • LineMesh 객체를 동적으로 생성하고, Player.txt 파일에서 데이터를 로드합니다.
  2. 맵에 추가:
    • _lineMeshes에 키(L"Player")와 LineMesh 객체를 저장합니다.
    • 이를 통해 키를 사용하여 리소스를 빠르게 검색할 수 있습니다.

void ResourceManager::Clear()
{
	for (auto mesh : _lineMeshes)
	{
		SAFE_DELETE(mesh.second);
	}
	_lineMeshes.clear();
}

코드 설명:

  1. 리소스 해제:
    • _lineMeshes의 모든 요소를 순회하며, 각 LineMesh 객체를 안전하게 삭제합니다.
    • SAFE_DELETE 매크로는 포인터를 삭제한 후 nullptr로 초기화하여 이중 삭제를 방지합니다.
  2. 맵 초기화:
    • _lineMeshes.clear()를 호출하여 모든 맵 항목을 제거합니다.

const LineMesh* ResourceManager::GetLineMesh(wstring key)
{
	auto findIt = _lineMeshes.find(key);
	if (findIt == _lineMeshes.end())
	{
		return nullptr;
	}

	return findIt->second;
}

코드 설명:

  1. 맵 검색:
    • _lineMeshes.find(key)를 사용하여 키에 해당하는 LineMesh 객체를 검색합니다.
    • 키를 찾지 못하면 nullptr을 반환합니다.
  2. 객체 반환:
    • 키를 찾으면 해당 LineMesh 객체를 반환합니다.

Game 클래스

게임의 전체 흐름을 관리하며, ResourceManager를 초기화하고 사용하는 코드입니다.

#include "pch.h"
#include "Game.h"
#include "TimeManager.h"
#include "InputManager.h"
#include "SceneManager.h"
#include "Utils.h"
#include "ResourceManager.h"

Game::Game()
{
}

Game::~Game()
{
	GET_SINGLE(SceneManager)->Clear();
	GET_SINGLE(ResourceManager)->Clear();

	_CrtDumpMemoryLeaks();
}

코드 설명:

  1. 소멸자:
    • SceneManagerResourceManager를 먼저 정리하여 리소스를 해제합니다.
    • _CrtDumpMemoryLeaks()는 메모리 누수를 감지하는 함수로, 프로그램 종료 시 누수가 발생하면 디버깅 정보가 출력됩니다.

void Game::Init(HWND hwnd)
{
	_hwnd = hwnd;
	_hdc = ::GetDC(hwnd);

	::GetClientRect(hwnd, &_rect);

	_hdcBack = ::CreateCompatibleDC(_hdc);
	_bmpBack = ::CreateCompatibleBitmap(_hdc, _rect.right, _rect.bottom);
	HBITMAP prev = (HBITMAP)::SelectObject(_hdcBack, _bmpBack);
	::DeleteObject(prev);

	GET_SINGLE(TimeManager)->Init();
	GET_SINGLE(InputManager)->Init(hwnd);
	GET_SINGLE(SceneManager)->Init();
	GET_SINGLE(ResourceManager)->Init();

	GET_SINGLE(SceneManager)->ChangeScene(SceneType::GameScene);
}

코드 설명:

  1. HWND 및 HDC 초기화:
    • HWND 핸들과 HDC를 통해 게임 윈도우의 렌더링 컨텍스트를 초기화합니다.
  2. 더블 버퍼링 초기화:
    • _hdcBack_bmpBack을 사용하여 더블 버퍼링을 구현합니다.
    • 더블 버퍼링은 화면 깜박임을 방지하는 기술입니다.
  3. 싱글톤 매니저 초기화:
    • TimeManager, InputManager, SceneManager, ResourceManager를 초기화합니다.
  4. Scene 변경:
    • 기본 씬을 GameScene으로 설정합니다.

void Game::Update()
{
	GET_SINGLE(TimeManager)->Update();
	GET_SINGLE(InputManager)->Update();
	GET_SINGLE(SceneManager)->Update();
}

코드 설명:

  1. 각 매니저 업데이트:
    • TimeManager, InputManager, SceneManager를 순서대로 업데이트하여 시간, 입력, 씬 변경 사항을 처리합니다.

void Game::Render()
{
	uint32 fps = GET_SINGLE(TimeManager)->GetFps();
	float deltaTime = GET_SINGLE(TimeManager)->GetDeltaTime();

	{
		POINT mousePos = GET_SINGLE(InputManager)->GetMousePos();
		wstring str = std::format(L"Mouse({0}, {1})", mousePos.x, mousePos.y);
		::TextOut(_hdcBack, 20, 10, str.c_str(), static_cast<int32>(str.size()));
	}
	{
		wstring str = std::format(L"FPS({0}), DT({1}) ms", fps, static_cast<int32>(deltaTime * 1000));
		::TextOut(_hdcBack, 650, 10, str.c_str(), static_cast<int32>(str.size()));
	}

	GET_SINGLE(SceneManager)->Render(_hdcBack);

	::BitBlt(_hdc, 0, 0, _rect.right, _rect.bottom, _hdcBack, 0, 0, SRCCOPY);
	::PatBlt(_hdcBack, 0, 0, _rect.right, _rect.bottom, WHITENESS);
}

코드 설명:

  1. FPS 및 마우스 위치 출력:
    • TimeManagerInputManager에서 데이터를 가져와 화면에 출력합니다.
  2. 씬 렌더링:
    • SceneManagerRender 메서드를 호출하여 현재 활성화된 씬을 그립니다.
  3. 더블 버퍼링:
    • BitBlt를 사용하여 백 버퍼의 내용을 화면으로 복사합니다.
    • PatBlt를 사용하여 백 버퍼를 초기화합니다.

Player 클래스의 ResourceManager 연동

void Player::Render(HDC hdc)
{
	const LineMesh* mesh = GET_SINGLE(ResourceManager)->GetLineMesh(L"Player");
	
	if (mesh)
	{
		mesh->Render(hdc, _pos);
	}
}

코드 설명:

  1. 리소스 검색:
    • ResourceManager에서 키(L"Player")를 사용하여 LineMesh 객체를 가져옵니다.
  2. 렌더링:
    • mesh->Render를 호출하여 플레이어 위치를 기준으로 선을 그립니다.

종합 분석

이 강의 코드는 리소스 관리의 기본적인 구조를 설명하며, 다음과 같은 핵심 개념을 다룹니다:
1. 리소스 관리:

  • 리소스 매니저를 통해 생성, 검색, 삭제가 효율적으로 관리됩니다.
  • 메모리 누수를 방지하며, 재사용성을 극대화합니다.
  1. 더블 버퍼링:
    • 깜박임 없는 부드러운 화면 렌더링이 가능합니다.
  2. 싱글톤 패턴:
    • 전역 관리 객체를 하나만 생성하여 사용합니다.

추가 질문이 있거나 다른 부분이 더 궁금하시면 말씀해주세요!

profile
李家네_공부방

0개의 댓글