전체 코드 : https://github.com/HyangRim/DirectX11-Engine-Client
GitHub - HyangRim/DirectX11-Engine-Client
Contribute to HyangRim/DirectX11-Engine-Client development by creating an account on GitHub.
github.com
Component 기반 게임 오브젝트 시스템
개요
이번 프로젝트에서는 Unity엔진과 유사하게 Component 기반으로 객체를 구성하였다.
이번 포스트에서는 직접 구현한 Component 시스템을 바탕으로 , GameObject - Component 구조 설계와 타입 안정성 및 성능을 고려한 Component 관리 기법을 알아보겠다.
1. GameObject - Component 구조 설계
1.1 기본 아키텍쳐 개요
Component 시스템의 핵심은 상속보다는 조합이다. 하나의 거대한 클래스 계층구조 대신, 작은 기능 단위의 컴포넌트들을 조합하여 게임 객체를 만들어낸다.
// GameObject.h - 메인 컨테이너 클래스
class GameObject : public enable_shared_from_this<GameObject>
{
public:
GameObject();
virtual ~GameObject();
virtual void Init();
virtual void Start();
virtual void Update();
virtual void LateUpdate();
virtual void FixedUpdate();
// 컴포넌트 관리
void AddComponent(shared_ptr<Component> _component);
shared_ptr<Component> GetFixedComponent(ComponentType _type);
// 타입 안전한 컴포넌트 접근
shared_ptr<Transform> GetTransform();
shared_ptr<Camera> GetCamera();
shared_ptr<MeshRenderer> GetMeshRenderer();
// ... 다른 컴포넌트 접근자들
private:
// 고정 컴포넌트 배열 (성능 최적화)
array<shared_ptr<Component>, FIXED_COMPONENT_COUNT> m_components;
// 동적 스크립트 컴포넌트
vector<shared_ptr<MonoBehaviour>> m_scripts;
wstring m_Name;
OBJECTTYPE m_type = OBJECTTYPE::NONE;
uint32 m_layerIndex = 0;
bool m_active = true;
};
1.2 Component 기본 클래스
모든 컴포넌트의 베이스가 되는 Component 클래스는 생명주기 관리와 GameObject 참조를 담당한다.
// Component.h - 모든 컴포넌트의 기반 클래스
class Component
{
public:
Component(ComponentType _type);
virtual ~Component();
virtual void Init() {}
virtual void Start() {}
virtual void Update() {}
virtual void LateUpdate() {}
virtual void FixedUpdate() {}
virtual void OnDestroy() {}
// GameObject 참조 관리
shared_ptr<GameObject> GetGameObject();
shared_ptr<Transform> GetTransform();
protected:
ComponentType m_type;
weak_ptr<GameObject> m_gameObject; // 순환 참조 방지
};
핵심 설계 포인트
- weak_ptr로 GameObject 참조하여 순환 참조 방지
- 각 컴포넌트는 고유한 ComponentType을 가짐
- 생명주기 메서드들을 가상함수로 제공하여 하위 클래스에서 재정의 가능
1.3 ComponentType 열거형과 타입 시스템
enum class ComponentType : uint8
{
Transform,
MeshRenderer,
ModelRenderer,
Camera,
Animator,
Light,
Collider,
// ... 고정 컴포넌트들
Script, // MonoBehaviour 경계
Custom,
End
};
enum
{
FIXED_COMPONENT_COUNT = static_cast<uint8>(ComponentType::End) - 1
};
이 설계의 장점
- 컴파일 타임 타입 안정성 : 잘못된 타입 접근을 컴파일 시점에 차단
- 메모리 효율성 : 고정 컴포넌트는 배열로, 가변 컴포넌트는 벡터로 분리
- 빠른 접근 속도 : 인덱스 기반 O(1) 접근
2. 타입 안정성과 성능을 고려한 컴포넌트 관리
2.1 이중 저장 구조 설계
성능과 유연성을 동시에 확보하기 위해 컴포넌트를 두 가지 방식으로 분리했다.
void GameObject::AddComponent(shared_ptr<Component> _component)
{
_component->SetGameObject(shared_from_this());
// MonoBehaviour인지 먼저 확인
auto script = dynamic_pointer_cast<MonoBehaviour>(_component);
if (script) {
m_scripts.push_back(script);
return;
}
uint8 index = static_cast<uint8>(_component->GetType());
if (index < FIXED_COMPONENT_COUNT) {
// 고정 컴포넌트: 배열에 저장 (O(1) 접근)
m_components[index] = _component;
}
else {
// 가변 컴포넌트: 벡터에 저장
m_scripts.push_back(dynamic_pointer_cast<MonoBehaviour>(_component));
}
}
성능 최적화 포인트
- 고정 컴포넌트 : Transform, Renderer 등 자주 접근하는 컴포넌트는 배열에 저장하여 O(1) 접근
- 가변 컴포넌트 : 사용자 정의 스크립트들은 벡터에 저장하여 동적 관리
- 메모리 지역성 : 관련 컴포넌트들이 메모리상에서 가깝게 배치
2.2 타입 안전한 컴포넌트 접근
각 컴포넌트 타입별로 전용 접근자를 제공하여 런타임 타입 체크 최소화
shared_ptr<Transform> GameObject::GetTransform()
{
shared_ptr<Component> component = GetFixedComponent(ComponentType::Transform);
if (component == nullptr) {
component = make_shared<Transform>();
AddComponent(component);
}
return static_pointer_cast<Transform>(component);
}
shared_ptr<MeshRenderer> GameObject::GetMeshRenderer()
{
shared_ptr<Component> component = GetFixedComponent(ComponentType::MeshRenderer);
return static_pointer_cast<MeshRenderer>(component);
}
shared_ptr<Renderer> GameObject::GetRenderer()
{
// 여러 렌더러 타입 중 하나를 반환
shared_ptr<Component> renderer = GetFixedComponent(ComponentType::MeshRenderer);
if (renderer == nullptr)
renderer = GetFixedComponent(ComponentType::ModelRenderer);
if (renderer == nullptr)
renderer = GetFixedComponent(ComponentType::Animator);
// ...
return static_pointer_cast<Renderer>(renderer);
}
2.3 메모리 관리와 생명주기
안전한 메모리 관리를 위한 여러 기법
void GameObject::OnDestroy()
{
m_isDestroyed = true;
// 1. 모든 컴포넌트들에게 OnDestroy 알림
for (auto& component : m_components) {
if (component) {
component->OnDestroy();
}
}
// 2. 스크립트들에게 OnDestroy 알림
for (auto& script : m_scripts) {
if (script) {
script->OnDestroy();
}
}
// 3. 참조 해제
ClearReferences();
}
void GameObject::ClearReferences()
{
// 컴포넌트들의 GameObject 참조 해제
for (auto& component : m_components) {
if (component) {
component->ClearGameObjectRef();
}
}
m_Name.clear();
m_alpha = 1.0f;
m_alphaChanged = false;
}
2.4 컴포넌트 시스템의 확장성
새로운 컴포넌트를 쉽게 추가할 수 있는 구조
// 새로운 컴포넌트 추가 예시
class InventoryManager : public Component
{
public:
InventoryManager() : Component(ComponentType::Custom) {}
virtual void Update() override
{
// 인벤토리 업데이트 로직
}
void AddItem(shared_ptr<Item> item) { /* 구현 */ }
void RemoveItem(int itemId) { /* 구현 */ }
};
// 사용법
auto player = make_shared<GameObject>();
player->AddComponent(make_shared<InventoryManager>());
3. 실제 활용
3.1 애니메이션 시스템 통합
Component 시스템과 State Machine의 결합 예시
shared_ptr<AnimationStateMachine> GameObject::GetAnimationStateMachine()
{
shared_ptr<Component> component = GetFixedComponent(ComponentType::AnimationStateMachine);
return static_pointer_cast<AnimationStateMachine>(component);
}
// 사용 예시
auto player = CreateCharacterBianca();
auto animStateMachine = player->GetAnimationStateMachine();
animStateMachine->RequestStateChange(AnimationStateType::Run);
3.2 충돌 시스템과의 연동
void GameObject::OnCollision(shared_ptr<GameObject> _other)
{
if (GetRigidbody() != nullptr) {
GetRigidbody()->OnCollision(_other);
}
}
void GameObject::OnCollisionEnter(shared_ptr<GameObject> _other)
{
// 모든 컴포넌트에게 충돌 이벤트 전파
for (auto& script : m_scripts) {
if (auto behaviorScript = dynamic_pointer_cast<MonoBehaviour>(script)) {
behaviorScript->OnCollisionEnter(_other);
}
}
}
결론
핵심 성과
- 타입 안정성 : ComponentType 열거형과 전용 접근자로 컴파일 타임 안정성 확보
- 성능 최적화 : 고정 / 가변 컴포넌트 분리로 메모리 접근 최적화
- 확장성 : 새로운 컴포넌트 추가가 용이한 구조
- 유지보수 : 기능별 분리로 코드 이해와 수정이 쉽다.
'DirectX11 > Eternal Return 모작' 카테고리의 다른 글
| [DirectX 11 Eternal Return 모작] 7. ResourceManager (0) | 2025.09.03 |
|---|---|
| [DirectX 11 Eternal Return 모작] 6. Scene (0) | 2025.09.02 |
| [DirectX 11 Eternal Return 모작] 4. Shader & Material (0) | 2025.09.02 |
| [DirectX 11 Eternal Return 모작] 3. 인스턴싱 (0) | 2025.09.02 |
| [DirectX 11 Eternal Return 모작] 2. FOW ( Fog Of War ) (0) | 2025.09.02 |