리소스매니저(ResourceManager) 클래스 생성
경로 : 2DFrameWork / GameObject / System / [추가]ResourceManager.h & ResourceManager.cpp
클래스 생성후 GameObject 필터에 framework.h 파일에 헤더및 매크로 추가하기.
ResourceManager.h
#pragma once
template<typename T>
class Resource // 리소스 클래스
{
private:
unordered_map<string, shared_ptr<T>> list; // 맵형태의 탬플릿 리스트
/*
탬플릿형태로 리스트를 만드는 이유? 리소스매니저를통하여 들어오는게 mesh일지 shader일지 모르기때문.
어떤 자료형이 들어와도 대응이 가능하도록 탬플릿사용.
현재 System필터의 클래스들은 대부분 탬플릿!
*/
public:
const shared_ptr<T>& Load(string file) // 파일불러오기 했을떄
{
auto it = list.find(file); // 리스트에서 파일을 검색하고
if (it != list.end()) // 찾는파일이 리스트의 끝(검색안됨)이아니라면 즉, 검색되었다면
{
return it->second; // 반환
}
shared_ptr<T> temp = make_shared<T>(); // 검색되지않았다면, 리스트에없다면 생성
temp->LoadFile(file);
list[file] = temp;
return list[file]; // 생성된것으로 반환
}
void ReleaseList() // 리스트 해제하기 (초기화)
{
for (auto it = list.begin(); it != list.end(); it++) // 리스트 처음부터 끝까지 해제
{
SafeReset(it->second); // Safe 매크로 ? 댕글링방지를위하여 해제후 nullptr로 바꿔줌!
}
list.clear(); // 해제가 완료된후 초기화!
}
};
class ResourceManager : public Singleton<ResourceManager> // 리소스매니저는 리소스들을 총괄한다.
{
public:
Resource<Mesh> meshes; // 매쉬
Resource<Shader> shaders; // 쉐이더
/*
리소스매니저에 Actor 리소스가 없는 이유
리소스의경우 스마트포인터 shared_ptr 로 만들어져있는데
이는 즉 모든 객체들이 포인터를통하여 참조(const shared_ptr<T>&)하고있기때문이다.
상수형태로 참조하는 탬플릿이기때문에 만일 한객체에서 정보를 변경할경우
모든객체의 정보가 변경되기때문에 매쉬나 쉐이더는 하나의 파일을 공유해도되지만
Actor의 경우 각각의 수행해야하는 것들이 다르기때문에 정보를 공유해서는 안된다!
*/
void ReleaseAll(); // 전체해제
};
ResourceManager.cpp
#include "framework.h"
void ResourceManager::ReleaseAll() // 생성, 추가, 탐색 등은 Resource에서 하기떄문에 해제만진행
{
meshes.ReleaseList();
shaders.ReleaseList();
}
저장&불러오기(GOSaveLoad.cpp)
경로 : 2DFrameWork / GameObject / GOSaveLoad.cpp
사용한 예시 XML 코드
더보기
<Root Name="Sun" Visible="true" ObType="1">
<Mesh File="1.Sphere.mesh"/>
<Shader File="1.Cube.hlsl"/>
<Transform>
<Position X="0" Y="0" Z="0"/>
<Scale X="1" Y="1" Z="1"/>
<Rotation X="-0.41887903" Y="-0.33161256" Z="1.2740904"/>
</Transform>
<Children Size="2"/>
<Child0 Name="EarthBone" Visible="true" ObType="0">
<Mesh File="1.Point.mesh"/>
<Shader File="1.Cube.hlsl"/>
<Transform>
<Position X="3.3499999" Y="-0.2" Z="-0.85000002"/>
<Scale X="0.5" Y="0.5" Z="0.5"/>
<Rotation X="-5" Y="276.43201" Z="0"/>
</Transform>
<Children Size="1"/>
<Child0 Name="Earth" Visible="true" ObType="0">
<Mesh File="1.Sphere.mesh"/>
<Shader File="1.Cube.hlsl"/>
<Transform>
<Position X="8.3999996" Y="1.15" Z="-1.3"/>
<Scale X="1" Y="1" Z="1"/>
<Rotation X="-1.1" Y="-309.23199" Z="-0.89999998"/>
</Transform>
<Children Size="1"/>
<Child0 Name="Moon" Visible="true" ObType="0">
<Mesh File="1.Sphere.mesh"/>
<Shader File="1.Cube.hlsl"/>
<Transform>
<Position X="2.55" Y="0" Z="0"/>
<Scale X="0.5" Y="0.5" Z="0.5"/>
<Rotation X="0" Y="0" Z="0"/>
</Transform>
<Children Size="0"/>
</Child0>
</Child0>
</Child0>
<Child1 Name="MarsBone" Visible="true" ObType="0">
<Mesh File="1.Point.mesh"/>
<Shader File="1.Cube.hlsl"/>
<Transform>
<Position X="0" Y="0" Z="0"/>
<Scale X="0.5" Y="0.5" Z="0.5"/>
<Rotation X="0" Y="0" Z="0"/>
</Transform>
<Children Size="1"/>
<Child0 Name="Mars" Visible="true" ObType="0">
<Mesh File="1.Sphere.mesh"/>
<Shader File="1.Cube.hlsl"/>
<Transform>
<Position X="12" Y="0" Z="0"/>
<Scale X="1" Y="1" Z="1"/>
<Rotation X="0" Y="0" Z="0"/>
</Transform>
<Children Size="0"/>
</Child0>
</Child1>
</Root>
#include "framework.h"
void Actor::SaveFile(string file) // 파일 저장하기
{
this->file = file; // 리스트는 문자열을 키로 사용한다. string file
Xml::XMLDocument* doc = new Xml::XMLDocument(); // doc 동적할당
Xml::XMLElement* ob = doc->NewElement("Root"); // 생성한 doc에 Element를 추가한다 (루트)
doc->LinkEndChild(ob); // doc의 자식노드로 ob(루트)를 붙여준다
SaveObject(ob, doc); // 오브젝트 저장하기 void GameObject::SaveObject(Xml::XMLElement* This, Xml::XMLDocument* doc)
string path = "../Contents/GameObject/" + file; // 경로생성
doc->SaveFile(path.c_str()); // 문자열형태로 경로이름 파일 저장
SafeDelete(doc); // 저장이끝나면 동적할당한 doc 해제
}
void Actor::LoadFile(string file) // 파일 불러오기
{
this->file = file; // Gui 에서 선택된 이름의 파일 불러오기
Xml::XMLDocument* doc = new Xml::XMLDocument(); // doc 동적할당
string path = "../Contents/GameObject/" + file; // 경로생성
// 불러오기 성공 = 이부분이있어야 불러올때 버벅임이 적다.
Xml::XMLError result = doc->LoadFile(path.c_str());
if (result != Xml::XML_SUCCESS) return;
//기존멤버지우기 = 기존에 있던정보와 겹치면 안되기 때문!
ReleaseMember();
Xml::XMLElement* ob; // Element 객체 생성
ob = doc->FirstChildElement(); // 동적할당한 doc에 첫번쨰 자식노드로 연결
Xml::XMLElement* component; // component 객체 생성
name = ob->Attribute("Name"); // 이름은 Name에 기제된 이름으로 설정
type = (ObType)ob->IntAttribute("ObType"); // 타입은 ObType으로 형변환해서 타입설정
obList[name] = this; // 리스트이름설정
LoadObject(ob); // 오브젝트 불러오기 void GameObject::LoadObject(Xml::XMLElement* This)
SafeDelete(doc); // 불러오기가 끝나면 동적할당 doc 해제
}
void GameObject::SaveObject(Xml::XMLElement* This, Xml::XMLDocument* doc) // 오브젝트 저장하기
{
// 예시 : <Root Name="Sun" Visible="true" ObType="1">
This->SetAttribute("Name", name.c_str()); // 이름 설정
This->SetAttribute("Visible", visible); // 비지블 설정
This->SetAttribute("ObType", (int)type); // 타입설정 (형변환)
if (mesh) // 매쉬는 매쉬로 저장 <Mesh File="1.Sphere.mesh"/>
{
Xml::XMLElement* Mesh = doc->NewElement("Mesh");
This->LinkEndChild(Mesh);
Mesh->SetAttribute("File", mesh->file.c_str());
}
if (shader) // 쉐이더는 쉐이더로 저장 <Shader File="1.Cube.hlsl"/>
{
Xml::XMLElement* Shader = doc->NewElement("Shader");
This->LinkEndChild(Shader);
Shader->SetAttribute("File", shader->file.c_str());
}
Transform::SaveTransform(This, doc); // 트랜스폼 저장
if (type == ObType::Camera) // 카메라가 있다면 카메라정보도 저장
{
Xml::XMLElement* Cam = doc->NewElement("Camera");
This->LinkEndChild(Cam);
Camera* CamOb = dynamic_cast<Camera*>(this);
Cam->SetAttribute("Ortho", CamOb->ortho);
Cam->SetAttribute("Fov", CamOb->fov);
Cam->SetAttribute("Width", CamOb->width);
Cam->SetAttribute("Height", CamOb->height);
Cam->SetAttribute("NearZ", CamOb->nearZ);
Cam->SetAttribute("FarZ", CamOb->farZ);
Cam->SetAttribute("viewportX", CamOb->viewport.x);
Cam->SetAttribute("viewportY", CamOb->viewport.y);
Cam->SetAttribute("viewportW", CamOb->viewport.width);
Cam->SetAttribute("viewportH", CamOb->viewport.height);
}
// 자식 노드가 있다면
Xml::XMLElement* Chidren = doc->NewElement("Children");
This->LinkEndChild(Chidren); // 현자자신의 자식으로 저장
Chidren->SetAttribute("Size", (int)children.size());
int i = 0; // 자식은 0번부터 <Child0 Name="EarthBone" Visible="true" ObType="0">
for (auto it = children.begin(); it != children.end(); it++)
{
string temp = "Child" + to_string(i++); // 자식이계속있다면 증가되면서추가됨
Xml::XMLElement* Child = doc->NewElement(temp.c_str());
This->LinkEndChild(Child);
it->second->SaveObject(Child, doc); // 재귀로 자식->자식 끝까지저장
}
}
void GameObject::LoadObject(Xml::XMLElement* This) // 오브젝트불러오기
{
// 불러오는정보는 다 준비되어있으니 검사로 진행한다.
Xml::XMLElement* component;
string file;
visible = This->BoolAttribute("Visible");
if (component = This->FirstChildElement("Mesh")) // Mesh 라면
{
file = component->Attribute("File"); // 예시 <Mesh File="1.Sphere.mesh"/>
SafeReset(mesh); // 기존에있던 mesh는 리셋 시키고
mesh = RESOURCE->meshes.Load(file); // 새롭게 리소스를 통해서 생성(리스트에있다면 불러오기)
}
if (component = This->FirstChildElement("Shader")) // Shader 라면 이하 동일
{
file = component->Attribute("File"); // 예시 <Shader File="1.Cube.hlsl"/>
SafeReset(shader); // 리셋후
shader = RESOURCE->shaders.Load(file); // 새롭게 추가
}
if (type == ObType::Camera) // 카메라라면
{
Camera* CamOb = dynamic_cast<Camera*>(this);
component = This->FirstChildElement("Camera");
CamOb->ortho = component->BoolAttribute("Ortho");
CamOb->fov = component->FloatAttribute("Fov");
CamOb->width = component->FloatAttribute("Width");
CamOb->height = component->FloatAttribute("Height");
CamOb->nearZ = component->FloatAttribute("NearZ");
CamOb->farZ = component->FloatAttribute("FarZ");
CamOb->viewport.x = component->FloatAttribute("viewportX");
CamOb->viewport.y = component->FloatAttribute("viewportY");
CamOb->viewport.width = component->FloatAttribute("viewportW");
CamOb->viewport.height = component->FloatAttribute("viewportH");
}
Transform::LoadTransform(This); // 트랜스폼 불러오기
component = This->FirstChildElement("Children");
int size = component->IntAttribute("Size"); // 자식의 수 받아오기 <Children Size="2"/>
for (int i = 0; i != size; i++) // 자식수만큼 반복실행
{
string Tag = "Child" + to_string(i);
Xml::XMLElement* ob = This->FirstChildElement(Tag.c_str());
string childName = ob->Attribute("Name");
ObType Type = (ObType)ob->IntAttribute("ObType");
//자식의 각 타입에 따라 추가하고 불러오기(리스트에없으면 생성됨)
if (Type == ObType::GameObject)
{
GameObject* temp = GameObject::Create(childName);
AddChild(temp);
temp->LoadObject(ob);
}
else if (Type == ObType::Actor)
{
Actor* temp = Actor::Create(childName);
AddChild(temp);
temp->LoadObject(ob);
}
else if (Type == ObType::Camera)
{
Camera* temp = Camera::Create(childName);
AddChild(temp);
temp->LoadObject(ob);
}
}
}
/*
트랜스폼 구간 예시 XML 코드
<Transform>
<Position X="3.3499999" Y="-0.2" Z="-0.85000002"/>
<Scale X="0.5" Y="0.5" Z="0.5"/>
<Rotation X="-5" Y="276.43201" Z="0"/>
*/
void Transform::SaveTransform(Xml::XMLElement* This, Xml::XMLDocument* doc) // 트랜스폼 저장하기
{
Xml::XMLElement* Trans = doc->NewElement("Transform");
Xml::XMLElement* Pos = doc->NewElement("Position");
Xml::XMLElement* Scale = doc->NewElement("Scale");
Xml::XMLElement* Rot = doc->NewElement("Rotation");
This->LinkEndChild(Trans); // Trans 를 자시긍로 추가하고
Trans->LinkEndChild(Pos); // Trans에 Pos를 자식으로 추가
Trans->LinkEndChild(Scale); // Trans에 Scale을 자식으로 추가
Trans->LinkEndChild(Rot); // Trans에 Rot을 자식으로 추가
// 각각의 정보에 접근 해서 저장
Pos->SetAttribute("X", position.x);
Pos->SetAttribute("Y", position.y);
Pos->SetAttribute("Z", position.z);
Scale->SetAttribute("X", scale.x);
Scale->SetAttribute("Y", scale.y);
Scale->SetAttribute("Z", scale.z);
Rot->SetAttribute("X", rotation.x);
Rot->SetAttribute("Y", rotation.y);
Rot->SetAttribute("Z", rotation.z);
}
void Transform::LoadTransform(Xml::XMLElement* This) // 트램스폼 불러오기
{
Xml::XMLElement* component;
Xml::XMLElement* transform;
component = This->FirstChildElement("Transform"); // 생성한 component Element에 Transform 넣기
transform = component->FirstChildElement("Position"); // component(Transform)의 자식인 Position을 transform에 넣기
position.x = transform->FloatAttribute("X"); // this의 포지션 x,y,z 에 읽어온 transform의 정보 넣기
position.y = transform->FloatAttribute("Y");
position.z = transform->FloatAttribute("Z");
transform = component->FirstChildElement("Scale");
scale.x = transform->FloatAttribute("X");
scale.y = transform->FloatAttribute("Y");
scale.z = transform->FloatAttribute("Z");
transform = component->FirstChildElement("Rotation");
rotation.x = transform->FloatAttribute("X");
rotation.y = transform->FloatAttribute("Y");
rotation.z = transform->FloatAttribute("Z");
}
정리 : 위와같이 파일단위로 Save Load 가 가능해졌기때문에
게임 오브젝트에서 mesh를 생성하거나 shader를 생성하지 않아도 된다!
- 맨처음 mesh, shader 만드는 방법 -
mesh = make_shared(); // 생성
shader = make_shared(); // 생성
shader->LoadFile("1.Cube.hlsl"); // 만들어진 hlsl 파일 불러와야했다.
* hlsl = high level shader langueage
- 그다음으로 리소스를 사용하여 불러오는 방법 -
shader = RESOURCE->shaders.Load("1.Cube.hlsl");
mesh = RESOURCE->meshes.Load("1.Sphere.mesh");
이제 위처럼 생성하거나 불러오는 작없이 필요없다.
'공부' 카테고리의 다른 글
[자료구조] 함수포인터 (0) | 2023.05.24 |
---|---|
[C++/DX11] 3D 가위바위보 (1) | 2023.05.21 |
[C++/DX11] Yaw Pitch Roll(요,피치,롤), Gimbal Lock(짐벌락), Quaternion(쿼터니언) (2) | 2023.05.16 |
[C++] 상속 접근 지정자 (Inheritance And Access Specifier) (0) | 2023.05.14 |
[C++] 특수 멤버 함수 (Special Member Function) (0) | 2023.05.14 |
댓글