Make Scene
Scene
씬(Scene)은 게임의 한 영역이나 레벨을 나타내는 컨테이너로, 게임 객체(GameObject)들의 집합체입니다.
씬 구조
// Scene.h의 핵심 구조
class Scene
{
public:
void Start();
void Update();
void LateUpdate();
public:
void AddGameObject(shared_ptr<GameObject> gameObject);
void AddBoneGameObject(shared_ptr<GameObject> boneGameObject);
void RemoveGameObject(shared_ptr<GameObject> gameObject);
void Picking();
void UIPicking();
void CheckCollision();
void AddPickedObject(shared_ptr<GameObject> pickedObject) { picked = pickedObject; }
void SetMainCamera(shared_ptr<GameObject> camera) { _mainCamera = camera; }
void SetSceneName(wstring sceneName) { _sceneName = sceneName; }
void SwitchMainCameraToEditorCamera() { _mainCamera = Find(L"EditorCamera"); }
void SwitchMainCameraToMainCamera() { _mainCamera = Find(L"MainCamera"); }
const vector<shared_ptr<GameObject>>& GetGameObjects() { return _gameObjects; }
shared_ptr<GameObject> Find(const wstring& name);
private:
vector<shared_ptr<GameObject>> _gameObjects;
vector<shared_ptr<GameObject>> _boneGameobjects;
wstring _sceneName = L"";
shared_ptr<GameObject> _mainCamera;
shared_ptr<GameObject> _mainLignt;
// ...
};
Scene XML 구조 예시
<!-- test_scene.xml의 일부 -->
<Scene>
<GameObject name="EditorCamera">
<Transform posX="-2" posY="2" posZ="-10" rotX="0" rotY="0" rotZ="0" scaleX="1" scaleY="1" scaleZ="1"/>
<Camera projectionType="0"/>
<Script type="EditorCamera"/>
</GameObject>
<GameObject name="MainCamera" parent="Kachujin_OBJ">
<Transform posX="-4.5776367e-05" posY="184.85187" posZ="257.14355" rotX="0" rotY="-1.3660378e-05" rotZ="0" scaleX="100" scaleY="100" scaleZ="100"/>
<Camera projectionType="0"/>
</GameObject>
<!-- 다른 GameObject들 -->
</Scene>
Scene 로드 방법
씬 로딩은 SceneManager 클래스의 LoadScene 메서드를 통해 이루어집니다.
// SceneManager.cpp
void SceneManager::LoadScene(wstring sceneName)
{
shared_ptr<Scene> scene = make_shared<Scene>();
_scenes[sceneName] = scene;
_activeScene = scene;
LoadSceneXML(sceneName);
SCENE.Init();
RENDER.Init();
}
void SceneManager::LoadSceneXML(wstring sceneName)
{
filesystem::path xmlPath = L"../Resource/Scene/" + sceneName + L".xml";
string path = Utils::ToString(xmlPath.generic_wstring());
tinyxml2::XMLDocument doc;
if (doc.LoadFile(path.c_str()) != tinyxml2::XML_SUCCESS)
return;
tinyxml2::XMLElement* root = doc.FirstChildElement("Scene");
if (root == nullptr)
return;
// 첫 번째 패스: GameObject 생성
map<wstring, shared_ptr<GameObject>> gameObjects;
map<wstring, wstring> parentNames;
for (tinyxml2::XMLElement* gameObjectElem = root->FirstChildElement("GameObject");
gameObjectElem != nullptr;
gameObjectElem = gameObjectElem->NextSiblingElement("GameObject"))
{
// GameObject 이름 가져오기
const char* nameAttr = gameObjectElem->Attribute("name");
if (nameAttr == nullptr)
continue;
// 부모 정보 가져오기
const char* parentAttr = gameObjectElem->Attribute("parent");
wstring name = Utils::ToWString(nameAttr);
shared_ptr<GameObject> gameObject = make_shared<GameObject>();
gameObject->SetName(name);
if (parentAttr != nullptr)
parentNames[name] = Utils::ToWString(parentAttr);
// 컴포넌트 추가 로직
// ...
// Scene에 GameObject 추가
_activeScene->AddGameObject(gameObject);
gameObjects[name] = gameObject;
}
// 두 번째 패스: 부모-자식 관계 설정
for (const auto& pair : parentNames)
{
const wstring& childName = pair.first;
const wstring& parentName = pair.second;
shared_ptr<GameObject> child = gameObjects[childName];
shared_ptr<GameObject> parent = gameObjects[parentName];
if (child && parent)
child->SetParent(parent);
}
}
Scene 저장 방법
씬 저장은 다양한 메서드들을 통해 GameObject 정보를 XML로 저장합니다.
// SceneManager.cpp
void SceneManager::SaveAndLoadGameObjectToXML(const wstring& sceneName, const wstring& name,
const Vec3& position, const Vec3& rotation, const Vec3& scale,
shared_ptr<GameObject> parent)
{
wstring path = L"Resource/Scene/" + sceneName + L".xml";
// XML에 저장
SaveGameObjectToXML(path, name, &position, &rotation, &scale, parent);
// 실제 GameObject 생성 및 Scene에 추가
shared_ptr<GameObject> gameObject = make_shared<GameObject>();
shared_ptr<Transform> transform = make_shared<Transform>();
transform->SetLocalPosition(position);
transform->SetLocalRotation(rotation);
transform->SetLocalScale(scale);
gameObject->AddComponent(transform);
if (parent != nullptr)
gameObject->SetParent(parent);
gameObject->SetName(name);
_activeScene->AddGameObject(gameObject);
}
void SceneManager::SaveGameObjectToXML(const wstring& path, const wstring& name, const Vec3* position, const Vec3* rotation, const Vec3* scale, const shared_ptr<GameObject>& parent)
{
if (ENGINE.GetEngineMode() != EngineMode::Edit)
return;
tinyxml2::XMLDocument doc;
// 기존 파일이 있으면 로드
string pathStr = Utils::ToString(path);
doc.LoadFile(pathStr.c_str());
tinyxml2::XMLElement* root = doc.FirstChildElement("Scene");
if (!root)
{
root = doc.NewElement("Scene");
doc.InsertFirstChild(root);
}
// 이미 존재하는 GameObject인지 확인
tinyxml2::XMLElement* existingObj = root->FirstChildElement("GameObject");
while (existingObj)
{
if (string(existingObj->Attribute("name")) == Utils::ToString(name))
{
root->DeleteChild(existingObj);
break;
}
existingObj = existingObj->NextSiblingElement("GameObject");
}
// GameObject 요소 생성
tinyxml2::XMLElement* gameObjectElem = doc.NewElement("GameObject");
gameObjectElem->SetAttribute("name", Utils::ToString(name).c_str());
if (parent)
gameObjectElem->SetAttribute("parent", Utils::ToString(parent->GetName()).c_str());
// Transform 정보 저장
tinyxml2::XMLElement* transformElem = doc.NewElement("Transform");
transformElem->SetAttribute("posX", position->x);
transformElem->SetAttribute("posY", position->y);
transformElem->SetAttribute("posZ", position->z);
transformElem->SetAttribute("rotX", rotation->x);
transformElem->SetAttribute("rotY", rotation->y);
transformElem->SetAttribute("rotZ", rotation->z);
transformElem->SetAttribute("scaleX", scale->x);
transformElem->SetAttribute("scaleY", scale->y);
transformElem->SetAttribute("scaleZ", scale->z);
gameObjectElem->InsertEndChild(transformElem);
root->InsertEndChild(gameObjectElem);
doc.SaveFile(pathStr.c_str());
}
컴포넌트 정보 업데이트를 위한 다양한 메서드들도 있습니다:
// SceneManager.cpp
void SceneManager::UpdateGameObjectTransformInXML(const wstring& sceneName, const wstring& objectName,
const Vec3& position, const Vec3& rotation, const Vec3& scale)
{
if (ENGINE.GetEngineMode() != EngineMode::Edit)
return;
tinyxml2::XMLDocument doc;
string pathStr = "Resource/Scene/" + Utils::ToString(sceneName) + ".xml";
doc.LoadFile(pathStr.c_str());
tinyxml2::XMLElement* root = doc.FirstChildElement("Scene");
if (!root)
return;
// GameObject 찾기
for (tinyxml2::XMLElement* gameObjectElem = root->FirstChildElement("GameObject");
gameObjectElem != nullptr;
gameObjectElem = gameObjectElem->NextSiblingElement("GameObject"))
{
const char* nameAttr = gameObjectElem->Attribute("name");
if (nameAttr && Utils::ToWString(nameAttr) == objectName)
{
// Transform 컴포넌트 찾기
tinyxml2::XMLElement* transformElem = gameObjectElem->FirstChildElement("Transform");
if (transformElem != nullptr)
{
// 속성 업데이트
transformElem->SetAttribute("posX", position.x);
transformElem->SetAttribute("posY", position.y);
transformElem->SetAttribute("posZ", position.z);
transformElem->SetAttribute("rotX", rotation.x);
transformElem->SetAttribute("rotY", rotation.y);
transformElem->SetAttribute("rotZ", rotation.z);
transformElem->SetAttribute("scaleX", scale.x);
transformElem->SetAttribute("scaleY", scale.y);
transformElem->SetAttribute("scaleZ", scale.z);
doc.SaveFile(path.c_str());
return;
}
// 다른 컴포넌트들...
}
}
}
새로운 씬 만드는 방법
새로운 Scene 생성은 CreateNewScene 메서드를 통해 이루어집니다:
// SceneManager.cpp
void SceneManager::CreateNewScene(wstring sceneName)
{
_isCreateNewScene = true;
string pathStr = "Resource/Scene/" + Utils::ToString(sceneName) + ".xml";
// 기존 XML 파일이 있다면 삭제
if (filesystem::exists(pathStr))
{
filesystem::remove(pathStr);
}
// EditorCamera
wstring path = L"Resource/Scene/" + sceneName + L".xml";
Vec3 editorCameraPos = Vec3(0.0f, 5.0f, -10.0f);
Vec3 editorCameraRotation = Vec3(22.0f, 0.0f, 0.0f);
Vec3 editorCameraScale = Vec3(1.0f, 1.0f, 1.0f);
SaveGameObjectToXML(path, L"EditorCamera", &editorCameraPos, &editorCameraRotation, &editorCameraScale, nullptr);
auto editorCameraComp = make_shared<Camera>();
editorCameraComp->SetProjectionType(ProjectionType::Perspective);
AddComponentToGameObjectAndSaveToXML(sceneName, L"EditorCamera", editorCameraComp);
AddComponentToGameObjectAndSaveToXML(sceneName, L"EditorCamera", make_shared<EditorCamera>());
// MainCamera
Vec3 mainCameraPos = Vec3(0.0f, 5.0f, -10.0f);
Vec3 mainCameraRotation = Vec3(22.0f, 0.0f, 0.0f);
Vec3 mainCameraScale = Vec3(1.0f, 1.0f, 1.0f);
SaveGameObjectToXML(path, L"MainCamera", &mainCameraPos, &mainCameraRotation, &mainCameraScale, nullptr);
auto camera = make_shared<Camera>();
camera->SetProjectionType(ProjectionType::Perspective);
AddComponentToGameObjectAndSaveToXML(sceneName, L"MainCamera", camera);
// UICamera
Vec3 uiCameraPos = Vec3(0, 0, -3);
Vec3 uiCameraRotation = Vec3::Zero;
Vec3 uiCameraScale = Vec3(1.0f, 1.0f, 1.0f);
SaveGameObjectToXML(path, L"UICamera", &uiCameraPos, &uiCameraRotation, &uiCameraScale, nullptr);
auto uiCamera = make_shared<Camera>();
uiCamera->SetProjectionType(ProjectionType::Orthographic);
AddComponentToGameObjectAndSaveToXML(sceneName, L"UICamera", uiCamera);
// MainLight
Vec3 mainLightPos = Vec3(-50.0f, 37.0f, 0.0f);
Vec3 mainLightRotation = Vec3::Zero;
Vec3 mainLightScale = Vec3(10.0f, 10.0f, 10.0f);
SaveGameObjectToXML(path, L"MainLight", &mainLightPos, &mainLightRotation, &mainLightScale, nullptr);
AddComponentToGameObjectAndSaveToXML(sceneName, L"MainLight", make_shared<Light>());
auto lightRenderer = make_shared<MeshRenderer>();
lightRenderer->SetMesh(RESOURCE.GetResource<Mesh>(L"Sphere"));
lightRenderer->SetModel(nullptr);
lightRenderer->SetMaterial(RESOURCE.GetResource<Material>(L"SimpleMaterial"));
lightRenderer->SetRasterzierState(D3D11_FILL_SOLID, D3D11_CULL_BACK, false);
lightRenderer->AddRenderPass();
lightRenderer->GetRenderPasses()[0]->SetPass(Pass::DEFAULT_RENDER);
lightRenderer->GetRenderPasses()[0]->SetMeshRenderer(lightRenderer);
lightRenderer->GetRenderPasses()[0]->SetTransform(_activeScene->Find(L"MainLight")->transform());
lightRenderer->GetRenderPasses()[0]->SetDepthStencilStateType(DSState::NORMAL);
AddComponentToGameObjectAndSaveToXML(sceneName, L"MainLight", lightRenderer,
L"SimpleMaterial", L"Sphere");
// SkyBox
Vec3 skyboxPosition = Vec3::Zero;
Vec3 skyboxRotation = Vec3::Zero;
Vec3 skyboxScale = Vec3::One;
SaveGameObjectToXML(path, L"skyBox", &skyboxPosition, &skyboxRotation, &skyboxScale, nullptr);
auto skyBoxRenderer = make_shared<MeshRenderer>();
skyBoxRenderer->SetMesh(RESOURCE.GetResource<Mesh>(L"Sphere"));
skyBoxRenderer->SetModel(nullptr);
skyBoxRenderer->SetMaterial(RESOURCE.GetResource<Material>(L"GrassSkybox_Material"));
skyBoxRenderer->SetRasterzierState(D3D11_FILL_SOLID, D3D11_CULL_BACK, true);
skyBoxRenderer->AddRenderPass();
skyBoxRenderer->GetRenderPasses()[0]->SetPass(Pass::DEFAULT_RENDER);
skyBoxRenderer->GetRenderPasses()[0]->SetMeshRenderer(skyBoxRenderer);
skyBoxRenderer->GetRenderPasses()[0]->SetTransform(_activeScene->Find(L"skyBox")->transform());
skyBoxRenderer->GetRenderPasses()[0]->SetDepthStencilStateType(DSState::NORMAL);
AddComponentToGameObjectAndSaveToXML(sceneName, L"skyBox", skyBoxRenderer,
L"GrassSkybox_Material", L"Sphere");
_isCreateNewScene = false;
}
씬 전환 방법
씬 전환은 LoadScene 메서드를 통해 이루어집니다:
// 이미 위에서 설명한 LoadScene 메서드를 통해 씬 전환
void SceneManager::LoadScene(wstring sceneName)
{
shared_ptr<Scene> scene = make_shared<Scene>();
_scenes[sceneName] = scene;
_activeScene = scene;
LoadSceneXML(sceneName);
SCENE.Init();
RENDER.Init();
}
엔진 초기화 시 SceneInfo.xml에서 시작 씬을 결정합니다:
// EngineBody.cpp
void EngineBody::Init(HWND hwnd, int width, int height)
{
// ... 초기화 코드 ...
wstring sceneName = L"";
tinyxml2::XMLDocument doc;
string pathStr = "../SceneInfo.xml";
// SceneInfo.xml 파일에서 시작 씬 이름 로드
if (doc.LoadFile(pathStr.c_str()) == tinyxml2::XML_SUCCESS) {
tinyxml2::XMLElement* root = doc.FirstChildElement("SceneInfo");
if (root) {
const char* sceneNameAttr = root->Attribute("sceneName");
if (sceneNameAttr) {
sceneName = Utils::ToWString(sceneNameAttr);
}
}
}
else {
// 파일이 없으면 기본값으로 생성
tinyxml2::XMLDocument newDoc;
tinyxml2::XMLElement* root = newDoc.NewElement("SceneInfo");
root->SetAttribute("sceneName", "test_scene");
newDoc.InsertFirstChild(root);
newDoc.SaveFile(pathStr.c_str());
}
// 초기 씬 로드
SCENE.LoadScene(sceneName);
_editScene = SCENE.GetActiveScene();
GUI.Init();
}
Last updated