# Prefab

{% embed url="<https://youtu.be/_4N8c7YNEXE?si=hU5PHMFwFjuyYWHP>" %}

## <mark style="background-color:yellow;">프리팹 생성 (CreatePrefabFromGameObject)</mark>

```
void GUIManager::CreatePrefabFromGameObject(const string& objectName)
{
    // Scene XML 로드
    wstring scenePath = L"Resource/Scene/" + SCENE.GetActiveScene()->GetSceneName() + L".xml";
    tinyxml2::XMLDocument sceneDoc;
    if (sceneDoc.LoadFile(Utils::ToString(scenePath).c_str()) != tinyxml2::XML_SUCCESS)
        return;

    // Prefab XML 문서 생성
    tinyxml2::XMLDocument prefabDoc;
    tinyxml2::XMLElement* prefabRoot = prefabDoc.NewElement("Prefab");
    prefabDoc.InsertFirstChild(prefabRoot);
    
    // 재귀적으로 게임 오브젝트 처리하는 savePrefab 함수 정의...
```

이 함수는 다음 단계로 동작합니다:

1. 현재 씬의 XML 문서 로드:
   1. 현재 활성화된 씬의 XML 파일을 로드합니다.
   2. 씬 파일에는 게임 오브젝트의 모든 정보(변환, 컴포넌트, 계층 구조 등)가 포함되어 있습니다.
2. 새로운 프리팹 XML 문서 생성:
   1. \<Prefab> 루트 요소를 가진 새 XML 문서를 만듭니다.
3. 대상 게임 오브젝트 찾기:
   1. 주어진 objectName으로 현재 씬에서 게임 오브젝트를 검색합니다.
4. 재귀적 저장 함수 구현:

```
   function<void(shared_ptr<GameObject>)> savePrefab =
       [&](shared_ptr<GameObject> gameObj)
   {
       // Scene XML에서 현재 GameObject 찾아서 저장
       tinyxml2::XMLElement* sceneGameObj = sceneDoc.FirstChildElement("Scene")->FirstChildElement("GameObject");
       while (sceneGameObj)
       {
           if (Utils::ToWString(sceneGameObj->Attribute("name")) == gameObj->GetName())
           {
               // GameObject 노드 생성 및 복사
               tinyxml2::XMLElement* newObj = prefabDoc.NewElement("GameObject");
               prefabRoot->InsertEndChild(newObj);
               CopyXMLElement(sceneGameObj, newObj, prefabDoc);
               break;
           }
           sceneGameObj = sceneGameObj->NextSiblingElement("GameObject");
       }

       for (const auto& child : gameObj->GetChildren())
       {
           savePrefab(child);
       }
   };
```

이 람다 함수는 다음과 같이 작동합니다:

* 씬 XML에서 게임 오브젝트를 찾습니다.
* 프리팹 XML에 새 게임 오브젝트 노드를 생성합니다.
* CopyXMLElement 함수를 사용하여 씬의 게임 오브젝트 XML 요소를 프리팹으로 복사합니다.
* 자식 게임 오브젝트에 대해 재귀적으로 이 과정을 반복합니다.

5. 저장 함수 호출 및 파일 저장:
   1. 루트 게임 오브젝트부터 시작하여 재귀적으로 저장 함수를 호출합니다.
   2. 완성된 프리팹 XML을 파일로 저장합니다.

## <mark style="background-color:yellow;">프리팹 로드 (LoadPrefabToScene)</mark>

```
shared_ptr<GameObject> SceneManager::LoadPrefabToScene(wstring prefab)
{
    // XML 파일 로드
    tinyxml2::XMLDocument doc;
    string pathStr = "Resource/Prefab/" + Utils::ToString(prefab) + ".xml";
    doc.LoadFile(pathStr.c_str());
    
    // ... 게임 오브젝트 생성 및 컴포넌트 설정 코드 ...
```

이 함수는 다음과 같은 과정을 수행합니다:

1. 프리팹 XML 로드:
   1. 프리팹 파일경로를 구성하고 XML 문서를 로드합니다.
2. 이름 중복 방지를 위한 매핑 생성:

```
   map<wstring, wstring> nameMapping;
```

* 기존 씬에 이미 존재하는 오브젝트와 이름 충돌을 방지하기 위한 매핑을 생성합니다.
* 원본 이름과 새 이름 간의 매핑을 저장합니다.

3. 모든 게임 오브젝트 생성:

```
   for (tinyxml2::XMLElement* gameObjElem = root->FirstChildElement("GameObject");
       gameObjElem; gameObjElem = gameObjElem->NextSiblingElement("GameObject"))
   {
       shared_ptr<GameObject> gameObj = make_shared<GameObject>();
       // ... 오브젝트 설정 및 컴포넌트 추가 ...
   }
```

이 루프에서는:

* 새 게임 오브젝트를 생성합니다.
* 이름 중복 확인 후 고유한 이름을 설정합니다.
* 이름 매핑을 업데이트합니다.
* 다양한 컴포넌트(Transform, Camera, Light, MeshRenderer, Collider 등)를 처리합니다.

4. 계층 구조 복원

```
   for (tinyxml2::XMLElement* gameObjElem = root->FirstChildElement("GameObject");
       gameObjElem; gameObjElem = gameObjElem->NextSiblingElement("GameObject"))
   {
       if (const char* parentAttr = gameObjElem->Attribute("parent"))
       {
           // ... 부모-자식 관계 복원 ...
       }
   }
```

부모-자식 관계를 읽고, 새 이름 매핑을 사용하여 관계를 복원합니다

## <mark style="background-color:yellow;">프리팹 시스템의 핵심 기술적 요소</mark>

* **XML을 이용한 데이터 저장과 불러오기**

이 프리팹 시스템은 TinyXML2 라이브러리를 사용하여 게임 오브젝트 계층 구조를 XML로 저장

합니다:

```
<Prefab>
    <GameObject name="cube">
        <Transform posX="8.4340391" posY="1.5573728" posZ="3.6571183" rotX="0" rotY="0" rotZ="0" scaleX="1" scaleY="1" scaleZ="1"/>
        <MeshRenderer useEnvironmentMap="false" fillMode="3" cullMode="3" frontCounterClockwise="false" material="SolidWhiteMaterial" mesh="Cube">
            <RenderPass pass="0" depthStencilState="0"/>
        </MeshRenderer>
        <BoxCollider scaleX="1" scaleY="1" scaleZ="1" centerX="0" centerY="0" centerZ="0"/>
    </GameObject>
    <GameObject name="sphere" parent="cube">
        <Transform posX="1.9596643" posY="1.1400769" posZ="-2.4572649" rotX="0" rotY="0" rotZ="0" scaleX="1" scaleY="1" scaleZ="1"/>
        <MeshRenderer useEnvironmentMap="false" fillMode="3" cullMode="3" frontCounterClockwise="false" material="SolidWhiteMaterial" mesh="Sphere">
            <RenderPass pass="0" depthStencilState="0"/>
        </MeshRenderer>
        <SphereCollider radius="0.5" centerX="0" centerY="0" centerZ="0"/>
        <Script type="TestEvent"/>
    </GameObject>
</Prefab>
```

* **이름 충돌 관리**

프리팹 인스턴스 생성 시 기존 씬의 오브젝트와 이름 충돌을 방지합니다:

```
int count = 1;
wstring baseName = name;
wstring newName = baseName;

// 이미 존재하는 오브젝트 이름인지 확인
while (_activeScene->Find(newName) != nullptr)
{
    newName = baseName + to_wstring(count);
    count++;
}
gameObj->SetName(newName);

nameMapping[name] = newName;
```

* **참조 무결성 유지**

이름 매핑을 사용하여 부모-자식 관계와 같은 참조 무결성을 유지합니다:

```
if (const char* parentAttr = gameObjElem->Attribute("parent"))
{
    wstring oldChildName = Utils::ToWString(gameObjElem->Attribute("name"));
    wstring oldParentName = Utils::ToWString(parentAttr);

    // nameMapping에서 새로운 이름 가져오기
    wstring newChildName = nameMapping[oldChildName];
    wstring newParentName = nameMapping[oldParentName];

    shared_ptr<GameObject> childObj = SCENE.GetActiveScene()->Find(newChildName);
    shared_ptr<GameObject> parentObj = SCENE.GetActiveScene()->Find(newParentName);

    if (childObj && parentObj)
    {
        childObj->SetParent(parentObj);
    }
}
```


---

# 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/prefab.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.
