-
[DirectX11] Component, Monobehaviour2024년 02월 17일
- 유니얼
-
작성자
-
2024.02.17.:22
728x90DirectX 11로 게임 엔진 아키텍처 만들기
게임 개발 과정에서 유니티 엔진과 유사한 컴포넌트 시스템을 구축하는 과정은 게임 개발에 있어 깊은 이해를 요구합니다. 본 글에서는 DirectX 11 환경에서 유니티의 게임 오브젝트와 컴포넌트 기반 아키텍처를 모방하여 자체 게임 엔진의 핵심을 구성해 보았습니다. 이러한 구현을 통해, 개발자는 DirectX 프로젝트 내에서 더욱 구조화되고 유연한 게임 Component 관리 시스템을 구축할 수 있습니다.
1. 기본 Component 클래스 구현
게임 엔진의 가장 기본적인 구성 요소는 Component 클래스입니다. 이 클래스는 모든 컴포넌트의 기반으로, 게임 오브젝트에 부여할 수 있는 다양한 기능(위치, 회전, 렌더링 등)을 정의합니다. Component 클래스는 다음과 같은 핵심 요소를 포함합니다:
- ComponentType 열거형: 각 컴포넌트의 유형을 정의합니다. 예를 들어 Transform, MeshRenderer 등이 있습니다.
- 생명주기 관리 함수: Awake(), Start(), Update() 등의 함수를 통해 컴포넌트의 상태를 관리합니다.
#pragma once #include <memory> // shared_ptr과 weak_ptr을 위한 헤더 포함 class GameObject; // GameObject 클래스의 전방 선언 class Transform; // Transform 클래스의 전방 선언 // 컴포넌트 유형을 나타내는 열거형 enum class ComponentType : uint8_t { Transform, MeshRenderer, Camera, Animator, Script, // 사용자 정의 스크립트를 위한 컴포넌트 유형 End, // 유효한 컴포넌트 유형의 끝을 표시, 총 개수 계산에 사용 }; // 고정된 컴포넌트 수를 정의 (Script 제외한 기본 컴포넌트) enum { FIXED_COMPONENT_COUNT = static_cast<uint8_t>(ComponentType::End) - 1 }; // Component 클래스 정의 class Component { public: Component(ComponentType type); // 생성자 virtual ~Component(); // 가상 소멸자 // 생명주기 메서드, 상속받는 컴포넌트에서 오버라이드 가능 virtual void Awake() {}; // 초기화 직전 호출 virtual void Start() {}; // 첫 번째 Update 호출 전 호출 virtual void Update() {}; // 매 프레임마다 호출 virtual void LateUpdate() {}; // 모든 Update 메서드 호출 후 호출 virtual void FixedUpdate() {}; // 고정된 시간 간격으로 호출 public: ComponentType GetType() { return _type; } // 컴포넌트 유형 반환 std::shared_ptr<GameObject> GetGameObject(); // 이 컴포넌트가 부착된 GameObject 반환 std::shared_ptr<Transform> GetTransform(); // 이 컴포넌트의 Transform 반환 private: friend class GameObject; // GameObject 클래스가 private 멤버에 접근할 수 있도록 함 void SetGameObject(std::shared_ptr<GameObject> gameObject) { _gameObject = gameObject; } // GameObject 설정 protected: ComponentType _type; // 컴포넌트 유형 std::weak_ptr<GameObject> _gameObject; // 이 컴포넌트를 소유한 GameObject에 대한 약한 참조 };
#include "pch.h" #include "Component.h" #include "GameObject.h" // 생성자 구현 Component::Component(ComponentType type) : _type(type) { } // 소멸자 구현 Component::~Component() { } // GetGameObject 구현: 이 컴포넌트가 부착된 GameObject의 shared_ptr 반환 std::shared_ptr<GameObject> Component::GetGameObject() { return _gameObject.lock(); // 약한 참조를 강한 참조로 변환하여 반환 } // GetTransform 구현: 이 컴포넌트가 부착된 GameObject의 Transform 컴포넌트 반환 std::shared_ptr<Transform> Component::GetTransform() { return _gameObject.lock()->GetTransform(); // GameObject의 Transform을 반환 }
2. MonoBehaviour
MonoBehaviour 클래스는 사용자 정의 스크립트를 위한 Component의 확장으로, Unity의 MonoBehaviour와 유사한 기능을 제공합니다. 이 클래스를 통해 개발자는 새로운 행동을 정의하고 게임 오브젝트의 동적인 행동을 제어할 수 있습니다.
#pragma once #include "Component.h" // 기본 컴포넌트 클래스 포함 // MonoBehaviour 클래스 정의: 사용자 정의 스크립트를 위한 기반 클래스 class MonoBehaviour : public Component { using Super = Component; // 부모 클래스에 대한 별칭 설정 public: MonoBehaviour(); // 생성자 ~MonoBehaviour(); // 소멸자 // 생명주기 메서드 오버라이드 virtual void Awake() override; // 초기화 직전에 호출됨 virtual void Update() override; // 매 프레임마다 호출됨 };
#include "pch.h" #include "MonoBehaviour.h" // MonoBehaviour의 생성자: ComponentType::Script 유형으로 부모 클래스 초기화 MonoBehaviour::MonoBehaviour() : Super(ComponentType::Script) { } MonoBehaviour::~MonoBehaviour() { } void MonoBehaviour::Awake() { // 초기화 코드 여기에 작성 } void MonoBehaviour::Update() { // 프레임마다 실행할 코드 여기에 작성 }
3. GameObject
GameObject 클래스는 다양한 Component들을 관리하고, 게임 오브젝트의 생명주기를 총괄하는 컨테이너 역할을 합니다. 이 클래스는 게임 개발에서 객체 지향적 접근 방식을 가능하게 하며, 복잡한 게임 로직을 간단한 컴포넌트의 조합으로 표현할 수 있게 해줍니다.
GameObject는 게임 오브젝트의 생성, 초기화, 업데이트를 관리하며, 각 컴포넌트의 생명주기 메서드를 호출합니다. 또한, 특정 유형의 컴포넌트를 추가하고 접근할 수 있는 메서드를 제공합니다.
#pragma once #include "Component.h" // 기본 컴포넌트 클래스 포함 class MonoBehaviour; class Transform; class Camera; class MeshRenderer; class Animator; class GameObject : public enable_shared_from_this<GameObject> { public: // 생성자: DirectX 디바이스와 컨텍스트를 받아 초기화 GameObject(ComPtr<ID3D11Device> device, ComPtr<ID3D11DeviceContext> deviceContext); // 소멸자 ~GameObject(); void Awake(); void Start(); // 업데이트 메서드: 게임 오브젝트의 로직 업데이트를 처리 void Update(); void LateUpdate(); void FixedUpdate(); shared_ptr<Component> GetFixedComponent(ComponentType type); shared_ptr<Transform> GetTransform(); shared_ptr<Transform> GetOrAddTransform(); void AddComponent(shared_ptr<Component> component); private: ComPtr<ID3D11Device> _device; // DirectX 디바이스 protected: array<shared_ptr<Component>, FIXED_COMPONENT_COUNT> _components; vector<shared_ptr<MonoBehaviour>> _scripts; };
#include "pch.h" #include "GameObject.h" #include "MonoBehaviour.h" #include "Transform.h" #include "Camera.h" #include "MeshRenderer.h" #include "Animator.h" // 생성자에서 게임 오브젝트의 초기화를 진행합니다. // 이 과정에서 기하 구조, 버퍼, 쉐이더, 텍스처 등을 설정합니다. GameObject::GameObject(ComPtr<ID3D11Device> device, ComPtr<ID3D11DeviceContext> deviceContext) : _device(device) { } GameObject::~GameObject() { } void GameObject::Awake() { for (shared_ptr<Component>& component : _components) { if (component) component->Awake(); } for (shared_ptr<MonoBehaviour>& script : _scripts) { script->Awake(); } } void GameObject::Start() { for (shared_ptr<Component>& component : _components) { if (component) component->Start(); } for (shared_ptr<MonoBehaviour>& script : _scripts) { script->Start(); } } // 업데이트 메서드에서는 게임 오브젝트의 위치, 회전 등을 업데이트합니다. void GameObject::Update() { for (shared_ptr<Component>& component : _components) { if (component) component->Update(); } for (shared_ptr<MonoBehaviour>& script : _scripts) { script->Update(); } } void GameObject::LateUpdate() { for (shared_ptr<Component>& component : _components) { if(component) component->LateUpdate(); } for (shared_ptr<MonoBehaviour>& script : _scripts) { script->LateUpdate(); } } void GameObject::FixedUpdate() { for (shared_ptr<Component>& component : _components) { if (component) component->FixedUpdate(); } for (shared_ptr<MonoBehaviour>& script : _scripts) { script->FixedUpdate(); } } shared_ptr<Component> GameObject::GetFixedComponent(ComponentType type) { uint8 index = static_cast<uint8>(type); assert(index < FIXED_COMPONENT_COUNT); return _components[index]; } shared_ptr<Transform> GameObject::GetTransform() { shared_ptr<Component> component = GetFixedComponent(ComponentType::Transform); return static_pointer_cast<Transform>(component); } shared_ptr<Transform> GameObject::GetOrAddTransform() { if (GetTransform() == nullptr) { shared_ptr<Transform> transform = make_shared<Transform>(); AddComponent(transform); } return GetTransform(); } void GameObject::AddComponent(shared_ptr<Component> component) { component->SetGameObject(shared_from_this()); uint8 index = static_cast<uint8>(component->GetType()); if (index < FIXED_COMPONENT_COUNT) { _components[index] = component; } else { _scripts.push_back(dynamic_pointer_cast<MonoBehaviour>(component)); } }
결론
이 블로그 글을 통해 C++과 DirectX 11을 활용하여 Unity의 컴포넌트 기반 아키텍처를 직접 구현하는 과정을 탐구했습니다. 우리는 Component, MonoBehaviour, GameObject 세 가지 핵심 클래스를 중심으로 구현 방법을 살펴보았고, 이를 통해 Unity의 유연하고 강력한 컴포넌트 시스템의 작동 원리를 이해할 수 있었습니다.
반응형다음글이전글이전 글이 없습니다.댓글