GameObject
GameObject의 정의와 역할
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는 다음과 같은 주요 기능을 제공합니다:
컴포넌트 관리 (추가/제거)
계층 구조 관리 (부모-자식 관계)
생명 주기 관리 (Start, Update, LateUpdate)
속성 관리 (이름, 타입 등)
SceneManager를 통한 GameObject 생성
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;
}
계층 구조 관리
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 계층도 함께 업데이트되어, 위치, 회전, 크기가 부모의 변환에 영향을 받게 됩니다.
XML을 통한 GameObject저장
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>
GameObject 활용 예시
다음은 코드에서 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);
Last updated