# Make Scene

{% embed url="<https://youtu.be/k-6TWXFG9XY>" %}

## <mark style="background-color:yellow;">Scene</mark>

씬(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>
```

## <mark style="background-color:yellow;">Scene 로드 방법</mark>

씬 로딩은 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);
    }
}
```

## <mark style="background-color:yellow;">Scene 저장 방법</mark>

씬 저장은 다양한 메서드들을 통해 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;
            }
            // 다른 컴포넌트들...
        }
    }
}
```

## <mark style="background-color:yellow;">새로운 씬 만드는 방법</mark>

새로운 Scene 생성은 CreateNewScene 메서드를 통해 이루어집니다:

<pre><code>// 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
<strong>    wstring path = L"Resource/Scene/" + sceneName + L".xml";
</strong>    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", &#x26;editorCameraPos, &#x26;editorCameraRotation, &#x26;editorCameraScale, nullptr);
    auto editorCameraComp = make_shared&#x3C;Camera>();
    editorCameraComp->SetProjectionType(ProjectionType::Perspective);
    AddComponentToGameObjectAndSaveToXML(sceneName, L"EditorCamera", editorCameraComp);
    AddComponentToGameObjectAndSaveToXML(sceneName, L"EditorCamera", make_shared&#x3C;EditorCamera>());

    // MainCamera
<strong>    Vec3 mainCameraPos = Vec3(0.0f, 5.0f, -10.0f);
</strong>    Vec3 mainCameraRotation = Vec3(22.0f, 0.0f, 0.0f);
    Vec3 mainCameraScale = Vec3(1.0f, 1.0f, 1.0f);
    SaveGameObjectToXML(path, L"MainCamera", &#x26;mainCameraPos, &#x26;mainCameraRotation, &#x26;mainCameraScale, nullptr);
    auto camera = make_shared&#x3C;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", &#x26;uiCameraPos, &#x26;uiCameraRotation, &#x26;uiCameraScale, nullptr);
    auto uiCamera = make_shared&#x3C;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", &#x26;mainLightPos, &#x26;mainLightRotation, &#x26;mainLightScale, nullptr);
    AddComponentToGameObjectAndSaveToXML(sceneName, L"MainLight", make_shared&#x3C;Light>());
    auto lightRenderer = make_shared&#x3C;MeshRenderer>();
    lightRenderer->SetMesh(RESOURCE.GetResource&#x3C;Mesh>(L"Sphere"));
    lightRenderer->SetModel(nullptr);
    lightRenderer->SetMaterial(RESOURCE.GetResource&#x3C;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", &#x26;skyboxPosition, &#x26;skyboxRotation, &#x26;skyboxScale, nullptr);
    auto skyBoxRenderer = make_shared&#x3C;MeshRenderer>();
    skyBoxRenderer->SetMesh(RESOURCE.GetResource&#x3C;Mesh>(L"Sphere"));
    skyBoxRenderer->SetModel(nullptr);
    skyBoxRenderer->SetMaterial(RESOURCE.GetResource&#x3C;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;
}
</code></pre>

## <mark style="background-color:yellow;">씬 전환 방법</mark>

씬 전환은 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();
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jihoon-jungs-organization.gitbook.io/jihoon_engine/feature-and-description/make-scene.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
