주제

  • 본 강의의 핵심 주제는 GameObject를 구성하는 다양한 기능들을 Component(컴포넌트) 라는 독립된 부품 단위로 설계하고 구현하는 것이다.
  • 유니티(Unity)의 GameObject + Component 구조를 모방하여, 조합 기반(Composition-based) 구조를 갖는 확장성 높은 게임 오브젝트 시스템을 구축한다.

개념

1. Component란?

  • Component는 GameObject에 부착되어 기능을 제공하는 부품 단위 클래스이다.
  • 예를 들어 위치를 담당하는 Transform, 렌더링을 담당하는 MeshRenderer, 시점을 담당하는 Camera, 사용자 정의 로직을 담당하는 Script가 있다.
  • 상속 기반의 Unreal 방식과 달리 Unity 방식은 빈 GameObject에 필요한 기능을 조립해서 완성하는 구조이며, 이는 유연성과 재사용성이 뛰어나다.

2. 구조 설계 원칙

  • 고정형 컴포넌트(Fixed): Transform, MeshRenderer, Camera, Animator는 하나만 존재 가능.
  • 동적 컴포넌트(Script 계열): Script(MonoBehaviour)는 여러 개 부착 가능.
  • Component는 Awake, Start, Update, LateUpdate, FixedUpdate 같은 생명 주기 함수를 통해 시점 기반 동작을 처리한다.

용어정리

용어설명
GameObject게임 월드 내 존재하는 모든 객체
ComponentGameObject에 부착되어 기능을 수행하는 부품
MonoBehaviour사용자 정의 Script 컴포넌트의 기본 클래스
ComponentTypeenum으로 컴포넌트 종류를 구분
Awake오브젝트 생성 시점에 초기화하는 함수
Start게임 실행 직후 한 번만 호출되는 초기 함수
Update매 프레임마다 호출되는 일반 갱신 함수
LateUpdateUpdate 이후 호출되는 후처리용 함수
FixedUpdate일정 간격으로 호출되는 물리 갱신용 함수
shared_from_this클래스 내부에서 자신을 shared_ptr로 안전하게 반환
weak_ptr참조용 스마트 포인터, 소유권 없음

코드 분석


1. ComponentType 정의 및 고정형 컴포넌트 갯수

enum class ComponentType : uint8
{
    Transform,
    MeshRenderer,
    Camera,
    Animator,
    Script,      // 여러 개 부착 가능
    End
};

enum
{
    FIXED_COMPONENT_COUNT = static_cast<uint8>(ComponentType::End) - 1
};
  • 고정형 컴포넌트는 총 4개이며, Script는 유일하게 여러 개 부착 가능한 컴포넌트FIXED_COMPONENT_COUNT 이후부터 관리된다.

2. Component 클래스 설계

class Component
{
public:
    Component(ComponentType type);
    virtual ~Component() {}

    virtual void Awake() {}
    virtual void Start() {}
    virtual void Update() {}
    virtual void LateUpdate() {}
    virtual void FixedUpdate() {}

    ComponentType GetType() { return _type; }

    void SetGameObject(shared_ptr<GameObject> gameObject) { _gameObject = gameObject; }

    shared_ptr<GameObject> GetGameObject();
    shared_ptr<Transform> GetTransform();

protected:
    ComponentType _type;
    weak_ptr<GameObject> _gameObject;
};
  • Awake~FixedUpdate: 생명 주기 메서드. 게임 흐름에 따라 자동 호출됨.
  • SetGameObject: 소유 GameObject를 설정한다.
  • GetTransform: 연결된 GameObject의 Transform을 간접적으로 반환한다.

3. MonoBehaviour 클래스 구현

class MonoBehaviour : public Component
{
public:
    MonoBehaviour() : Component(ComponentType::Script) {}
    virtual void Awake() override {}
    virtual void Update() override {}
};
  • Script 전용 컴포넌트로, 사용자 정의 스크립트 로직을 구현할 기본 클래스이다.
  • 직접 GameObject에 붙여서 사용할 수 있으며, Update() 등을 재정의하여 동작시킨다.

4. GameObject 클래스와 컴포넌트 배열

class GameObject : public enable_shared_from_this<GameObject>
{
protected:
    std::array<shared_ptr<Component>, FIXED_COMPONENT_COUNT> _components;
    std::vector<shared_ptr<MonoBehaviour>> _scripts;

public:
    void AddComponent(shared_ptr<Component> component);
    shared_ptr<Component> GetFixedComponent(ComponentType type);
    shared_ptr<Transform> GetTransform();
    shared_ptr<Transform> GetOrAddTransform();

    void Awake();
    void Start();
    void Update();
    void LateUpdate();
    void FixedUpdate();
};
  • _components: 고정형 컴포넌트들 저장 (Transform, Camera 등)
  • _scripts: MonoBehaviour를 여러 개 저장하는 리스트

5. AddComponent 구현

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));
}
  • 반드시 enable_shared_from_this를 상속해야 shared_from_this() 사용 가능
  • ComponentType에 따라 _components 혹은 _scripts에 분류하여 삽입

6. Transform 가져오기 및 자동 생성

shared_ptr<Transform> GameObject::GetTransform()
{
    return static_pointer_cast<Transform>(GetFixedComponent(ComponentType::Transform));
}

shared_ptr<Transform> GameObject::GetOrAddTransform()
{
    if (!GetTransform())
        AddComponent(make_shared<Transform>());
    return GetTransform();
}
  • Transform은 GameObject에 반드시 존재해야 함
  • 없을 경우 자동으로 생성해서 부착

7. 컴포넌트 생명 주기 실행

void GameObject::Update()
{
    for (auto& comp : _components)
        if (comp) comp->Update();

    for (auto& script : _scripts)
        script->Update();

    _transformData.matWorld = GetOrAddTransform()->GetWorldMatrix();
    _constantBuffer->CopyData(_transformData);
}
  • 모든 컴포넌트들을 순회하면서 생명 주기 메서드를 호출한다.
  • Awake, Start, LateUpdate, FixedUpdate 역시 동일한 방식으로 구현

8. Transform 클래스의 강화

class Transform : public Component
{
public:
    Transform() : Component(ComponentType::Transform) {}

    Vec3 GetRight() { return _matWorld.Right(); }
    Vec3 GetUp()    { return _matWorld.Up(); }
    Vec3 GetLook()  { return _matWorld.Backward(); }
};
  • _right, _up, _look캐싱하지 않고 행렬에서 실시간 계산
  • 좌표계 방향에 따라 Right, Up, Backward를 선택함

9. TransformNormal vs TransformCoord

함수설명
TransformNormal방향 벡터 변환 (w=0), 위치 무시
TransformCoord좌표 변환 (w=1), 위치까지 포함
  • 방향(look, up, right) 계산 시에는 반드시 TransformNormal을 사용해야 한다.

핵심

  1. Component는 GameObject의 기능을 수행하는 독립된 부품 클래스이다.
  2. GameObject는 실제 게임 엔티티이며, 모든 기능은 Component 조합을 통해 구현된다.
  3. 고정형 컴포넌트와 동적 컴포넌트로 구조를 나누어 효율적 관리가 가능하다.
  4. Unity 스타일의 생명주기 함수(Awake, Start, Update 등)를 지원하여 시간 기반의 흐름을 통합할 수 있다.
  5. shared_from_this()weak_ptr 기반 연결로 스마트 포인터의 안전성 확보.
  6. Transform, Camera, MonoBehaviour 등 모든 기능은 Component로 확장 가능하며, 재사용성과 유지보수성을 극대화할 수 있다.

profile
李家네_공부방

0개의 댓글