# GameObject

{% embed url="<https://youtu.be/WnDjmDCxNR0>" %}

## <mark style="background-color:yellow;">GameObject의 정의와 역할</mark>

GameObject는 게임 엔진에서 가장 기본적인 개체로, 게임 세계의 모든 요소(캐릭터, 카메라, 조명 등)를 표현합니다. Unity와 유사한 컴포넌트 기반 아키텍처를 채택하여 확장성과 유연성을 제공합니다.

```
// GameObject.h에서 정의된 기본 구조
class GameObject : public enable_shared_from_this<GameObject>
{
public:
    GameObject();
    ~GameObject();

    void Start();
    void Update();
    void LateUpdate();

    // 컴포넌트 관리 메서드
    void AddComponent(shared_ptr<Component> component);
    void RemoveComponent(shared_ptr<Component> component);
    
    // 계층 구조 관리 메서드
    void SetParent(shared_ptr<GameObject> parent);
    void AddChild(shared_ptr<GameObject> child);
    
    // 기타 속성 관리
    void SetName(const wstring& name) { _name = name; }
    wstring& GetName() { return _name; }
    
    // ... 기타 메서드들 ...

private:
    vector<shared_ptr<Component>> _components;
    vector<shared_ptr<GameObject>> _children;
    wstring _name;
    GameObjectType _type;
    shared_ptr<GameObject> _parent;
    // ... 기타 멤버 변수들 ...
};
```

GameObject는 다음과 같은 주요 기능을 제공합니다:

1. 컴포넌트 관리 (추가/제거)
2. 계층 구조 관리 (부모-자식 관계)
3. 생명 주기 관리 (Start, Update, LateUpdate)
4. 속성 관리 (이름, 타입 등)

## <mark style="background-color:yellow;">SceneManager를 통한 GameObject 생성</mark>

SceneManager는 다양한 유형의 GameObject를 생성하는 편리한 방법을 제공합니다:

```
// SceneManager.h에 정의된 생성 메서드들
shared_ptr<GameObject> CreateCubeToScene(const wstring& sceneName);
shared_ptr<GameObject> CreateSphereToScene(const wstring& sceneName);
shared_ptr<GameObject> CreateCylinderToScene(const wstring& sceneName);
shared_ptr<GameObject> CreateQuadToScene(const wstring& sceneName);
shared_ptr<GameObject> CreateGridToScene(const wstring& sceneName);
shared_ptr<GameObject> CreateTerrainToScene(const wstring& sceneName);
shared_ptr<GameObject> CreateParticleToScene(const wstring& sceneName);
shared_ptr<GameObject> CreateAnimatedMeshToScene(const wstring& sceneName, const wstring& modelName);
shared_ptr<GameObject> CreateStaticMeshToScene(const wstring& sceneName, const wstring& modelName);
```

예를 들어, 큐브 오브젝트 생성 과정을 살펴보겠습니다:

```
shared_ptr<GameObject> SceneManager::CreateCubeToScene(const wstring& sceneName)
{
	// 새로운 오브젝트 생성 시 기본 이름 설정
	int count = 1;
	wstring baseName = L"cube";
	wstring newName = baseName;

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

	SaveAndLoadGameObjectToXML(sceneName, newName,
		Vec3(0.0f, 0.0f, 0.0f));
	auto cubeRenderer = make_shared<MeshRenderer>();
	cubeRenderer->SetMesh(RESOURCE.GetResource<Mesh>(L"Cube"));
	cubeRenderer->SetModel(nullptr);
	cubeRenderer->SetMaterial(RESOURCE.GetResource<Material>(L"SolidWhiteMaterial"));
	cubeRenderer->SetRasterzierState(D3D11_FILL_SOLID, D3D11_CULL_BACK, false);
	cubeRenderer->AddRenderPass();
	cubeRenderer->GetRenderPasses()[0]->SetPass(Pass::DEFAULT_RENDER);
	cubeRenderer->GetRenderPasses()[0]->SetMeshRenderer(cubeRenderer);
	cubeRenderer->GetRenderPasses()[0]->SetTransform(_activeScene->Find(newName)->transform());
	cubeRenderer->GetRenderPasses()[0]->SetDepthStencilStateType(DSState::NORMAL);
	AddComponentToGameObjectAndSaveToXML(sceneName, newName, cubeRenderer,
		L"SolidWhiteMaterial", L"Cube");
	auto boxCollider = make_shared<BoxCollider>();
	boxCollider->SetScale(Vec3(1.0f, 1.0f, 1.0f));
	AddComponentToGameObjectAndSaveToXML(sceneName, newName, boxCollider);

	RENDER.GetRenderableObject();

	return _activeScene->Find(newName);
}
```

SceneManager에서 위와 같이 오브젝트 추가를 호출하게되면 다음과 같은 작업을 Scene에서 처리합니다:

```
// Scene.cpp에 정의된 메서드들
void Scene::AddGameObject(shared_ptr<GameObject> gameObject)
{
    _gameObjects.push_back(gameObject);
}

void Scene::RemoveGameObject(shared_ptr<GameObject> gameObject)
{
    gameObject->DetachFromParent();

    // 특수 관계 처리 (본 오브젝트 등)
    shared_ptr<GameObject> nonBoneChildrenParent = gameObject->GetNoneBoneChildrenParent();
    if (nonBoneChildrenParent)
    {
        nonBoneChildrenParent->GetBoneParentObject().lock()->RemoveActiveBoneIndex(nonBoneChildrenParent->GetBoneIndex());
        nonBoneChildrenParent->SetHasNoneBoneChildrenFlag(false);
    }

    // 자식 오브젝트 재귀적 제거
    vector<shared_ptr<GameObject>> children = gameObject->GetChildren(); // 복사본 생성
    for (shared_ptr<GameObject> child : children)
    {
        child->DetachFromParent();
        RemoveGameObject(child); // 재귀적으로 자식들도 제거
        SCENE.RemoveGameObjectFromXML(SCENE.GetActiveScene()->GetSceneName(), child->GetName());
    }

    // 목록에서 제거
    auto it = std::find(_gameObjects.begin(), _gameObjects.end(), gameObject);
    if (it != _gameObjects.end())
    {
        _gameObjects.erase(it);
    }

    // 본 오브젝트 목록에서도 확인하여 제거
    it = std::find(_boneGameobjects.begin(), _boneGameobjects.end(), gameObject);
    if (it != _boneGameobjects.end())
    {
        _boneGameobjects.erase(it);
    }
    
    // 렌더링 목록 업데이트
    RENDER.GetRenderableObject();
}
```

Scene을 통해 GameObject를 검색할 수도 있습니다:

```
shared_ptr<GameObject> Scene::Find(const wstring& name)
{
    for (shared_ptr<GameObject> gameObject : _gameObjects)
    {
        if (gameObject->GetName() == name)
            return gameObject;
    }
    for (shared_ptr<GameObject> gameObject : _boneGameobjects)
    {
        if (gameObject->GetName() == name)
            return gameObject;
    }
    return nullptr;
}

shared_ptr<GameObject> Scene::FindWithComponent(ComponentType type)
{
    for (shared_ptr<GameObject> gameObject : _gameObjects)
    {
        switch (type)
        {
            case ComponentType::Transform:
                if (gameObject->GetComponent<Transform>() != nullptr)
                    return gameObject;
                break;
            case ComponentType::MeshRenderer:
                if (gameObject->GetComponent<MeshRenderer>() != nullptr)
                    return gameObject;
                break;
            case ComponentType::Camera:
                if (gameObject->GetComponent<Camera>() != nullptr)
                {
                    if (GUI.isSceneView())
                    {
                        if (gameObject->GetName() != L"EditorCamera")
                            continue;
                    else
                        return gameObject;
                    }
                else
                {
                    if (gameObject->GetName() != L"MainCamera")
                        continue;
                    else
                        return gameObject;
                    }
                }
                break;
            case ComponentType::Animator:
                if (gameObject->GetComponent<Animator>() != nullptr)
                    return gameObject;
                break;
            case ComponentType::Light:
                if (gameObject->GetComponent<Light>() != nullptr)
                    return gameObject;
                break;
            default:
                break;
        }
    }
    return nullptr;
}
```

## <mark style="background-color:yellow;">계층 구조 관리</mark>

GameObject는 부모-자식 관계를 통해 계층적으로 구성될 수 있습니다:

```
void GameObject::SetParent(shared_ptr<GameObject> parent)
{
    DetachFromParent();

    _parent = parent;
    shared_from_this()->transform()->SetParent(_parent->transform());
    _parent->AddChild(shared_from_this());
    _parent->transform()->AddChild(shared_from_this()->transform());
}

void GameObject::AddChild(shared_ptr<GameObject> child)
{
    _children.push_back(child);
}

void GameObject::DetachFromParent()
{
    if (_parent)
    {
        // 부모의 자식 목록에서 제거
        auto it = std::find(_parent->_children.begin(), _parent->_children.end(), shared_from_this());
        if (it != _parent->_children.end())
            _parent->_children.erase(it);

        // Transform 계층도 분리
        transform()->DetachFromParent();

        _parent = nullptr;
    }
}
```

부모가 변경되면 Transform 계층도 함께 업데이트되어, 위치, 회전, 크기가 부모의 변환에 영향을 받게 됩니다.

### <mark style="background-color:yellow;">XML을 통한 GameObject저장</mark>

SceneManager는 GameObject를 XML 형태로 저장하고 로드합니다:

```
void SceneManager::SaveAndLoadGameObjectToXML(const wstring& sceneName, const wstring& name,
    const Vec3& position, const Vec3& rotation, const Vec3& scale, shared_ptr<GameObject> parent)
{
    // 파일 경로 결정
    wstring xmlPath = L"../GameCoding/Resource/Scene/" + sceneName + L".xml";
    string strPath(xmlPath.begin(), xmlPath.end());
    
    // XML 문서 로드 또는 생성
    tinyxml2::XMLDocument doc;
    if (filesystem::exists(strPath))
        doc.LoadFile(strPath.c_str());
    else
    {
        tinyxml2::XMLDeclaration* decl = doc.NewDeclaration();
        doc.InsertFirstChild(decl);
        
        tinyxml2::XMLElement* sceneElem = doc.NewElement("Scene");
        doc.InsertEndChild(sceneElem);
    }
    
    // 씬 요소 가져오기
    tinyxml2::XMLElement* sceneElem = doc.FirstChildElement("Scene");
    
    // 게임 오브젝트 생성 및 씬에 추가
    shared_ptr<GameObject> gameObject = make_shared<GameObject>();
    gameObject->SetName(name);
    gameObject->transform()->SetLocalPosition(position);
    gameObject->transform()->SetLocalRotation(rotation);
    gameObject->transform()->SetLocalScale(scale);
    
    // 부모 설정
    if (parent)
        gameObject->SetParent(parent);
    
    GetActiveScene()->AddGameObject(gameObject);
    
    // XML에 게임 오브젝트 정보 저장
    tinyxml2::XMLElement* gameObj = doc.NewElement("GameObject");
    gameObj->SetAttribute("name", string(name.begin(), name.end()).c_str());
    
    // Transform 정보 저장
    tinyxml2::XMLElement* transformElem = doc.NewElement("Transform");
    
    tinyxml2::XMLElement* posElem = doc.NewElement("Position");
    posElem->SetAttribute("x", position.x);
    posElem->SetAttribute("y", position.y);
    posElem->SetAttribute("z", position.z);
    transformElem->InsertEndChild(posElem);
    
    tinyxml2::XMLElement* rotElem = doc.NewElement("Rotation");
    rotElem->SetAttribute("x", rotation.x);
    rotElem->SetAttribute("y", rotation.y);
    rotElem->SetAttribute("z", rotation.z);
    transformElem->InsertEndChild(rotElem);
    
    tinyxml2::XMLElement* scaleElem = doc.NewElement("Scale");
    scaleElem->SetAttribute("x", scale.x);
    scaleElem->SetAttribute("y", scale.y);
    scaleElem->SetAttribute("z", scale.z);
    transformElem->InsertEndChild(scaleElem);
    
    gameObj->InsertEndChild(transformElem);
    sceneElem->InsertEndChild(gameObj);
    
    // 부모-자식 관계 저장
    if (parent)
    {
        tinyxml2::XMLElement* parentElem = doc.NewElement("Parent");
        parentElem->SetAttribute("name", string(parent->GetName().begin(), parent->GetName().end()).c_str());
        gameObj->InsertEndChild(parentElem);
    }
    
    // XML 저장
    doc.SaveFile(strPath.c_str());
}
```

저장된 XML형태는 다음과 같습니다:

```
<GameObject name="cube">
 <Transform posX="40.368896" posY="4.1358805" posZ="44.429325" rotX="0" rotY="0" rotZ="0" scaleX="1" scaleY="1" scaleZ="1"/>
  <MeshRenderer useEnvironmentMap="false" fillMode="3" cullMode="3" frontCounterClockwise="false" material="DefaultMaterial" mesh="Sphere">
   <RenderPass pass="1" depthStencilState="1"/>
  </MeshRenderer>
 <BoxCollider scaleX="1" scaleY="1" scaleZ="1" centerX="0" centerY="0" centerZ="0"/>
</GameObject>
```

## <mark style="background-color:yellow;">GameObject 활용 예시</mark>

다음은 코드에서 GameObject를 활용하는 몇 가지 예시입니다:

```
// 씬에서 게임 오브젝트 찾기
shared_ptr<GameObject> camera = scene->Find(L"MainCamera");

// 컴포넌트 접근
shared_ptr<Camera> cameraComponent = camera->GetComponent<Camera>();
shared_ptr<Transform> transform = camera->transform();

// 위치 변경
transform->SetLocalPosition(Vec3(0, 5, -10));

// 새 컴포넌트 추가
shared_ptr<BoxCollider> collider = make_shared<BoxCollider>();
camera->AddComponent(collider);

// 부모-자식 관계 설정
shared_ptr<GameObject> child = make_shared<GameObject>();
child->SetName(L"ChildObject");
child->SetParent(camera);

// 사용자정의 컴포넌트 추가
shared_ptr<MoveObject> moveScript = make_shared<MoveObject>();
child->AddComponent(moveScript);

// 씬에 추가
scene->AddGameObject(child);
```


---

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