-
[DirectX11] ResourceManager2024년 03월 04일
- 유니얼
-
작성자
-
2024.03.04.:35
728x90DirectX 11로 게임 엔진 아키텍처 만들기
이번 블로그 글에서는 ResourceManager와 Resource관리에 대해 알아보고자 합니다. 특히, DirectX 11을 사용하여 텍스처, 메시, 셰이더와 같은 자원들을 효율적으로 관리하는 것을 구현하는 것을 목표로 만들었습니다. 이를 위해 ResourceManager 구현 방법과 함께, ResourceBase와ResourceManager 의 역할과 구현 방법에 대해 상세히 다루어, 게임의 구조를 보다 효율적으로 관리하는 방법을 탐구할 예정입니다.
ResourceBase 클래스
ResourceBase는 모든 자원의 기본 클래스입니다. 이 클래스는 자원의 공통 속성과 메서드를 정의합니다.
ResourceBase.h
#pragma once // 자원 유형을 나타내는 열거형 enum class ResourceType : uint8 { None = -1, Mesh, Shader, Texture, Material, Animation, End, }; // 사용 가능한 자원 유형의 수 enum { RESOURCE_TYPE_COUNT = static_cast<uint8>(ResourceType::End) }; // 모든 자원의 기본 클래스 class ResourceBase { public: ResourceBase(ResourceType type); // 생성자 virtual ~ResourceBase(); // 가상 소멸자 ResourceType GetType() { return _type; } // 자원 유형 반환 void SetName(const wstring& name) { _name = name; } // 자원 이름 설정 const wstring& GetName() { return _name; } // 자원 이름 반환 uint32 GetId() { return _id; } // 자원 ID 반환 protected: virtual void Load(const wstring& path) {} // 자원 로딩 (가상 함수) virtual void Save(const wstring& path) {} // 자원 저장 (가상 함수) protected: ResourceType _type = ResourceType::None; // 자원 유형 wstring _name; // 자원 이름 wstring _path; // 자원 경로 uint32 _id = 0; // 자원 ID };
ResourceBase.cpp
#include "pch.h" #include "ResourceBase.h" ResourceBase::ResourceBase(ResourceType type) : _type(type) { } ResourceBase::~ResourceBase() { }
ResourceManager 클래스 개요
ResourceManager 클래스는 게임 내 모든 자원(텍스처, 메시, 셰이더, 재료, 애니메이션 등)을 관리합니다. 이 클래스는 자원의 로딩, 저장, 검색, 해제를 담당하며, 자원의 재사용을 최대화하여 성능을 최적화합니다.
클래스 설계
ResourceManager 클래스의 주요 구성 요소는 다음과 같습니다:
- 자원 저장소: array<KeyObject, RESOURCE_TYPE_COUNT>를 사용하여 다양한 유형의 자원을 저장합니다. 여기서 KeyObject는 map<wstring, shared_ptr<ResourceBase>> 타입입니다.
- 디바이스 컨텍스트: DirectX 11 디바이스 컨텍스트(ComPtr<ID3D11Device>)를 저장하여 자원 생성에 사용합니다.
- 템플릿 기반 로딩 및 접근: 자원의 유형에 따라 템플릿 함수를 제공하여 로딩(Load), 추가(Add), 검색(Get)을 수행합니다.
핵심 기능 구현
1, 자원 로딩
자원을 로딩하는 과정은 키와 경로를 기반으로 합니다. 자원이 이미 저장소에 있는지 확인한 후, 존재하지 않는 경우 새로 로딩하여 저장소에 추가합니다.
// 자원 로딩 메서드 구현 template<typename T> inline shared_ptr<T> ResourceManager::Load(const wstring& key, const wstring& path) { // 자원 유형을 결정 ResourceType resourceType = GetResourceType<T>(); KeyObject& keyObjMap = _resources[static_cast<uint8>(resourceType)]; // 이미 로드된 자원 검색 auto findIt = keyObjMap.find(key); if (findIt != keyObjMap.end()) { return static_pointer_cast<T>(findIt->second); } // 새 자원 로드 및 저장 shared_ptr<T> object = make_shared<T>(_device); object->Load(path); keyObjMap[key] = object; return object; }
2, 자원 추가 및 검색
자원을 저장소에 추가하는 Add 메서드와, 저장된 자원을 검색하는 Get 메서드를 구현합니다. 이 메서드들은 자원의 유형을 자동으로 식별하여 적절한 저장소에 액세스합니다.
// 자원 추가 메서드 구현 template<typename T> inline bool ResourceManager::Add(const wstring& key, shared_ptr<T> object) { ResourceType resourceType = GetResourceType<T>(); KeyObject& keyObjMap = _resources[static_cast<uint8>(resourceType)]; auto findIt = keyObjMap.find(key); if (findIt != keyObjMap.end()) { return false; // 이미 존재하는 키 } keyObjMap[key] = object; // 자원 추가 return true; } // 자원 검색 메서드 구현 template<typename T> inline shared_ptr<T> ResourceManager::Get(const wstring& key) { ResourceType resourceType = GetResourceType<T>(); KeyObject& keyObjMap = _resources[static_cast<uint8>(resourceType)]; auto findIt = keyObjMap.find(key); if (findIt != keyObjMap.end()) { return static_pointer_cast<T>(findIt->second); } return nullptr; // 자원을 찾지 못함 } // 자원 유형 반환 메서드 구현 template<typename T> inline ResourceType ResourceManager::GetResourceType() { // T 유형에 따라 적절한 ResourceType 반환 if (std::is_same_v<T, Mesh>) return ResourceType::Mesh; if (std::is_same_v<T, Shader>) return ResourceType::Shader; if (std::is_same_v<T, Texture>) return ResourceType::Texture; if (std::is_same_v<T, Material>) return ResourceType::Material; if (std::is_same_v<T, Animation>) return ResourceType::Animation; assert(false); // 지원되지 않는 유형 return ResourceType::None; }
3, 기본 자원 생성
엔진 초기화 시 기본 자원(텍스처, 메시, 셰이더, 재료, 애니메이션)을 생성합니다. 이는 개발자가 즉시 사용할 수 있는 기본 세트를 제공합니다.
// 기본 텍스처 생성 함수 void ResourceManager::CreateDefaultTexture() { // Unity 로고 텍스처 생성 { auto texture = make_shared<Texture>(_device); // Texture 객체 생성 texture->SetName(L"UnityLogo"); // 텍스처 이름 설정 texture->Create(L"UnityLogo.png"); // 텍스처 파일 로드 Add(texture->GetName(), texture); // ResourceManager에 텍스처 추가 } // Snake 이미지 텍스처 생성 { auto texture = make_shared<Texture>(_device); // Texture 객체 생성 texture->SetName(L"Snake"); // 텍스처 이름 설정 texture->Create(L"Snake.bmp"); // 텍스처 파일 로드 Add(texture->GetName(), texture); // ResourceManager에 텍스처 추가 } } // 기본 메시 생성 함수 void ResourceManager::CreateDefaultMesh() { // Rectangle 메시 생성 { shared_ptr<Mesh> mesh = make_shared<Mesh>(_device); // Mesh 객체 생성 mesh->SetName(L"Rectangle"); // 메시 이름 설정 mesh->CreateDefaultRectangle(); // 기본 사각형 메시 생성 Add(mesh->GetName(), mesh); // ResourceManager에 메시 추가 } } // 기본 쉐이더 생성 함수 void ResourceManager::CreateDefaultShader() { auto vertexShader = make_shared<VertexShader>(_device); // VertexShader 객체 생성 vertexShader->Create(L"Default.hlsl", "VS", "vs_5_0"); // 정점 쉐이더 로드 auto inputLayout = make_shared<InputLayout>(_device); // InputLayout 객체 생성 inputLayout->Create(VertexTextureData::descs, vertexShader->GetBlob()); // 입력 레이아웃 생성 auto pixelShader = make_shared<PixelShader>(_device); // PixelShader 객체 생성 pixelShader->Create(L"Default.hlsl", "PS", "ps_5_0"); // 픽셀 쉐이더 로드 // Shader 객체 생성 및 설정 shared_ptr<Shader> shader = make_shared<Shader>(); shader->SetName(L"Default"); // 쉐이더 이름 설정 shader->_vertexShader = vertexShader; // 정점 쉐이더 설정 shader->_inputLayout = inputLayout; // 입력 레이아웃 설정 shader->_pixelShader = pixelShader; // 픽셀 쉐이더 설정 Add(shader->GetName(), shader); // ResourceManager에 쉐이더 추가 } // 기본 재료 생성 함수 void ResourceManager::CreateDefaultMaterial() { shared_ptr<Material> material = make_shared<Material>(); // Material 객체 생성 material->SetName(L"Default"); // 재료 이름 설정 material->SetShader(Get<Shader>(L"Default")); // Default 쉐이더 설정 material->SetTexture(Get<Texture>(L"UnityLogo")); // UnityLogo 텍스처 설정 Add(material->GetName(), material); // ResourceManager에 재료 추가 } // 기본 애니메이션 생성 함수 void ResourceManager::CreateDefaultAnimation() { shared_ptr<Animation> animation = make_shared<Animation>(); // Animation 객체 생성 animation->SetName(L"SnakeAnim"); // 애니메이션 이름 설정 animation->SetTexture(Get<Texture>(L"Snake")); // Snake 텍스처 설정 animation->SetLoop(true); // 반복 설정 // 애니메이션 키프레임 추가 animation->AddKeyFrame(KeyFrame{ Vec2{0.f,0.f}, Vec2{100.f,100.f}, 0.1f }); animation->AddKeyFrame(KeyFrame{ Vec2{100.f,0.f}, Vec2{100.f,100.f}, 0.1f }); animation->AddKeyFrame(KeyFrame{ Vec2{200.f,0.f}, Vec2{100.f,100.f}, 0.1f }); animation->AddKeyFrame(KeyFrame{ Vec2{300.f,0.f}, Vec2{100.f,100.f}, 0.1f }); Add(animation->GetName(), animation); // ResourceManager에 애니메이션 추가 // 애니메이션 저장 및 로드 예제 (XML, JSON 등을 사용할 수 있음) animation->Save(L"TestAnim.xml"); // 애니메이션 상태 저장 shared_ptr<Animation> anim2 = make_shared<Animation>(); // 새 Animation 객체 생성 anim2->Load(L"TestAnim.xml"); // 저장된 애니메이션 상태 로드 }
이 예시는 매우 기본적인 구조를 보여줍니다. 실제 게임 엔진에서는 자원의 종류가 다양하고, 로딩 및 관리 로직이 더 복잡할 수 있습니다.
전체코드
ResourceManager.h
#pragma once #include "ResourceBase.h" // 필요한 클래스 선언 class Mesh; class Shader; class Texture; class Material; class Animation; // 자원 관리자 클래스 class ResourceManager { public: ResourceManager(ComPtr<ID3D11Device> device); // 생성자 void Init(); // 초기화 메서드 // 자원 로딩 template<typename T> shared_ptr<T> Load(const wstring& key, const wstring& path); // 자원 추가 template<typename T> bool Add(const wstring& key, shared_ptr<T> object); // 자원 검색 template<typename T> shared_ptr<T> Get(const wstring& key); // 자원 유형 반환 template<typename T> ResourceType GetResourceType(); private: // 기본 자원 생성 메서드 void CreateDefaultTexture(); void CreateDefaultMesh(); void CreateDefaultShader(); void CreateDefaultMaterial(); void CreateDefaultAnimation(); private: ComPtr<ID3D11Device> _device; // DirectX 11 디바이스 // 자원 저장을 위한 컨테이너 using KeyObject = map<wstring, shared_ptr<ResourceBase>>; array<KeyObject, RESOURCE_TYPE_COUNT> _resources; }; // 자원 로딩 메서드 구현 template<typename T> inline shared_ptr<T> ResourceManager::Load(const wstring& key, const wstring& path) { // 자원 유형을 결정 ResourceType resourceType = GetResourceType<T>(); KeyObject& keyObjMap = _resources[static_cast<uint8>(resourceType)]; // 이미 로드된 자원 검색 auto findIt = keyObjMap.find(key); if (findIt != keyObjMap.end()) { return static_pointer_cast<T>(findIt->second); } // 새 자원 로드 및 저장 shared_ptr<T> object = make_shared<T>(_device); object->Load(path); keyObjMap[key] = object; return object; } // 자원 추가 메서드 구현 template<typename T> inline bool ResourceManager::Add(const wstring& key, shared_ptr<T> object) { ResourceType resourceType = GetResourceType<T>(); KeyObject& keyObjMap = _resources[static_cast<uint8>(resourceType)]; auto findIt = keyObjMap.find(key); if (findIt != keyObjMap.end()) { return false; // 이미 존재하는 키 } keyObjMap[key] = object; // 자원 추가 return true; } // 자원 검색 메서드 구현 template<typename T> inline shared_ptr<T> ResourceManager::Get(const wstring& key) { ResourceType resourceType = GetResourceType<T>(); KeyObject& keyObjMap = _resources[static_cast<uint8>(resourceType)]; auto findIt = keyObjMap.find(key); if (findIt != keyObjMap.end()) { return static_pointer_cast<T>(findIt->second); } return nullptr; // 자원을 찾지 못함 } // 자원 유형 반환 메서드 구현 template<typename T> inline ResourceType ResourceManager::GetResourceType() { // T 유형에 따라 적절한 ResourceType 반환 if (std::is_same_v<T, Mesh>) return ResourceType::Mesh; if (std::is_same_v<T, Shader>) return ResourceType::Shader; if (std::is_same_v<T, Texture>) return ResourceType::Texture; if (std::is_same_v<T, Material>) return ResourceType::Material; if (std::is_same_v<T, Animation>) return ResourceType::Animation; assert(false); // 지원되지 않는 유형 return ResourceType::None; }
ResourceManager.cpp
#include "pch.h" // 프로젝트의 사전 컴파일 헤더 파일 #include "ResourceManager.h" // ResourceManager 클래스 정의 포함 #include "Texture.h" // Texture 클래스 정의 포함 #include "Mesh.h" // Mesh 클래스 정의 포함 #include "Shader.h" // Shader 클래스 정의 포함 #include "Material.h" // Material 클래스 정의 포함 #include "Animation.h" // Animation 클래스 정의 포함 // ResourceManager 클래스 생성자 ResourceManager::ResourceManager(ComPtr<ID3D11Device> device) : _device(device) // DirectX 11 디바이스를 멤버 변수에 할당 { } // 자원 관리자 초기화 함수 void ResourceManager::Init() { CreateDefaultTexture(); // 기본 텍스처 생성 CreateDefaultMesh(); // 기본 메시 생성 CreateDefaultShader(); // 기본 쉐이더 생성 CreateDefaultMaterial(); // 기본 재료 생성 CreateDefaultAnimation(); // 기본 애니메이션 생성 } // 기본 텍스처 생성 함수 void ResourceManager::CreateDefaultTexture() { // Unity 로고 텍스처 생성 { auto texture = make_shared<Texture>(_device); // Texture 객체 생성 texture->SetName(L"UnityLogo"); // 텍스처 이름 설정 texture->Create(L"UnityLogo.png"); // 텍스처 파일 로드 Add(texture->GetName(), texture); // ResourceManager에 텍스처 추가 } // Snake 이미지 텍스처 생성 { auto texture = make_shared<Texture>(_device); // Texture 객체 생성 texture->SetName(L"Snake"); // 텍스처 이름 설정 texture->Create(L"Snake.bmp"); // 텍스처 파일 로드 Add(texture->GetName(), texture); // ResourceManager에 텍스처 추가 } } // 기본 메시 생성 함수 void ResourceManager::CreateDefaultMesh() { // Rectangle 메시 생성 { shared_ptr<Mesh> mesh = make_shared<Mesh>(_device); // Mesh 객체 생성 mesh->SetName(L"Rectangle"); // 메시 이름 설정 mesh->CreateDefaultRectangle(); // 기본 사각형 메시 생성 Add(mesh->GetName(), mesh); // ResourceManager에 메시 추가 } } // 기본 쉐이더 생성 함수 void ResourceManager::CreateDefaultShader() { auto vertexShader = make_shared<VertexShader>(_device); // VertexShader 객체 생성 vertexShader->Create(L"Default.hlsl", "VS", "vs_5_0"); // 정점 쉐이더 로드 auto inputLayout = make_shared<InputLayout>(_device); // InputLayout 객체 생성 inputLayout->Create(VertexTextureData::descs, vertexShader->GetBlob()); // 입력 레이아웃 생성 auto pixelShader = make_shared<PixelShader>(_device); // PixelShader 객체 생성 pixelShader->Create(L"Default.hlsl", "PS", "ps_5_0"); // 픽셀 쉐이더 로드 // Shader 객체 생성 및 설정 shared_ptr<Shader> shader = make_shared<Shader>(); shader->SetName(L"Default"); // 쉐이더 이름 설정 shader->_vertexShader = vertexShader; // 정점 쉐이더 설정 shader->_inputLayout = inputLayout; // 입력 레이아웃 설정 shader->_pixelShader = pixelShader; // 픽셀 쉐이더 설정 Add(shader->GetName(), shader); // ResourceManager에 쉐이더 추가 } // 기본 재료 생성 함수 void ResourceManager::CreateDefaultMaterial() { shared_ptr<Material> material = make_shared<Material>(); // Material 객체 생성 material->SetName(L"Default"); // 재료 이름 설정 material->SetShader(Get<Shader>(L"Default")); // Default 쉐이더 설정 material->SetTexture(Get<Texture>(L"UnityLogo")); // UnityLogo 텍스처 설정 Add(material->GetName(), material); // ResourceManager에 재료 추가 } // 기본 애니메이션 생성 함수 void ResourceManager::CreateDefaultAnimation() { shared_ptr<Animation> animation = make_shared<Animation>(); // Animation 객체 생성 animation->SetName(L"SnakeAnim"); // 애니메이션 이름 설정 animation->SetTexture(Get<Texture>(L"Snake")); // Snake 텍스처 설정 animation->SetLoop(true); // 반복 설정 // 애니메이션 키프레임 추가 animation->AddKeyFrame(KeyFrame{ Vec2{0.f,0.f}, Vec2{100.f,100.f}, 0.1f }); animation->AddKeyFrame(KeyFrame{ Vec2{100.f,0.f}, Vec2{100.f,100.f}, 0.1f }); animation->AddKeyFrame(KeyFrame{ Vec2{200.f,0.f}, Vec2{100.f,100.f}, 0.1f }); animation->AddKeyFrame(KeyFrame{ Vec2{300.f,0.f}, Vec2{100.f,100.f}, 0.1f }); Add(animation->GetName(), animation); // ResourceManager에 애니메이션 추가 // 애니메이션 저장 및 로드 예제 (XML, JSON 등을 사용할 수 있음) animation->Save(L"TestAnim.xml"); // 애니메이션 상태 저장 shared_ptr<Animation> anim2 = make_shared<Animation>(); // 새 Animation 객체 생성 anim2->Load(L"TestAnim.xml"); // 저장된 애니메이션 상태 로드 }
결론
ResourceManager 클래스는 게임 엔진에서 자원을 효율적으로 관리하는 데 필수적인 구성 요소입니다. DirectX 11과 같은 그래픽 API를 사용하여 자원 관리 시스템을 구현함으로써, 게임의 성능을 최적화하고 개발 과정을 보다 수월하게 만들 수 있습니다.
반응형다음글이전글이전 글이 없습니다.댓글