가장 바깥 구조

Unreal에서 Actor는 혼자서 모든 기능을 들고 있는 객체가 아니다.
실제 게임 로직은 Actor가 소유한 Component로 나뉘고, Actor는 그 Component를 조립해서 하나의 게임 오브젝트처럼 쓰는 경우가 많다.

UObject
  └─ UActorComponent
       └─ USceneComponent
            └─ UPrimitiveComponent
  • UActorComponent는 재사용 가능한 기능 단위다.
  • USceneComponent는 transform을 가진다.
  • UPrimitiveComponent는 렌더링이나 충돌에 직접 연결되는 geometry 계층이다.

UActorComponent

UActorComponent는 가장 기본적인 컴포넌트다.
공식 API 설명도 reusable behavior를 Actor에 추가하는 기본 단위라는 식으로 정의한다.

이 계층에서는 아직 위치나 회전 같은 transform 개념이 없다.

UCLASS()
class UMyStatusComponent : public UActorComponent
{
    GENERATED_BODY()

public:
    virtual void BeginPlay() override;
    virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};
  • Actor에 붙어서 lifecycle을 공유한다.
  • 필요하면 Actor처럼 BeginPlay, TickComponent를 가진다.

다만 Tick이 자동으로 되는 건 아니다.
공식 문서 기준으로 TickComponent()는 컴포넌트가 등록돼 있어야 하고, PrimaryComponentTick.bCanEverTick이 켜져 있어야 실행된다.

USceneComponent

USceneComponent부터는 transform이 생긴다.
즉 위치, 회전, 스케일을 가지는 계층이다.

Unreal의 Actor는 transform을 직접 들고 있지않고, 보통 RootComponent를 통해 transform을 표현한다.
Actor는 RootComponent를 통해 위치 정보를 제공한다.

RootComponent = CreateDefaultSubobject(TEXT("Root"));

SpringArm = CreateDefaultSubobject(TEXT("SpringArm"));
SpringArm->SetupAttachment(RootComponent);

Camera = CreateDefaultSubobject(TEXT("Camera"));
Camera->SetupAttachment(SpringArm);
  • RootComponent가 기준 transform을 잡고
  • 그 아래 SceneComponent들이 attachment 트리를 만들고
  • 자식은 부모 기준 상대 transform으로 움직인다

UPrimitiveComponent

UPrimitiveComponent는 렌더링이나 충돌에 직접 관여하는 계층이다.
geometry를 가지거나 생성해서 렌더링 또는 collision data로 쓰는 컴포넌트다.

  • UStaticMeshComponent
  • USkeletalMeshComponent
  • UBoxComponent
  • USphereComponent
  • UCapsuleComponent

화면에 보이거나, 보이지 않더라도 충돌 판정에 참여할 때 사용된다.

Mesh = CreateDefaultSubobject(TEXT("Mesh"));
Mesh->SetupAttachment(RootComponent);

Collision = CreateDefaultSubobject(TEXT("Collision"));
Collision->SetupAttachment(RootComponent);

둘 다 SceneComponent 계층이지만

  • Mesh는 시각 표현이 중심이다.
  • BoxComponent는 trigger나 collision volume 역할이 중심이다.

둘 다 world에 배치되고, attachment를 가지며, transform을 공유하는 PrimitiveComponent다.


생성 구조

Unreal에서 컴포넌트를 만드는 가장 기본적인 위치는 Actor 생성자다.
여기서 CreateDefaultSubobject()로 기본 컴포넌트를 만들고, attachment와 기본 속성을 정리한다.

AMyActor::AMyActor()
{
    PrimaryActorTick.bCanEverTick = true;

    RootComponent = CreateDefaultSubobject(TEXT("Root"));

    Mesh = CreateDefaultSubobject(TEXT("Mesh"));
    Mesh->SetupAttachment(RootComponent);

    MoveComponent = CreateDefaultSubobject(TEXT("MoveComponent"));
}

Actor는 생성자에서 필요한 컴포넌트를 붙이며 역할을 나눈다.

  • Actor: 조립과 소유
  • ActorComponent: 상태나 순수 로직
  • SceneComponent: 위치가 필요한 기능
  • PrimitiveComponent: 렌더링 또는 충돌

등록과 초기화

InitializeComponent()는 레벨 시작이나 Actor spawn 시점에, BeginPlay보다 먼저 일어난다.
등록된 컴포넌트 기준으로 초기화가 필요한 것들을 준비하는 구간이다.

virtual void InitializeComponent() override;
virtual void BeginPlay() override;
  • 생성자에서 기본 subobject 구성
  • 컴포넌트 등록
  • 필요하면 InitializeComponent()
  • 이후 BeginPlay()

InitializeComponent()는 모든 플레이가 시작되기 전 준비 단계라는 성격이 강하고,
BeginPlay()는 실제 게임이 시작된 뒤 동작을 여는 구간이라는 차이가 있다.


BeginPlay 흐름

UActorComponent::BeginPlay()는 owning Actor가 BeginPlay에 들어갈 때 호출된다.
이미 BeginPlay가 지난 Actor에 동적으로 컴포넌트를 만들면 그 시점에 BeginPlay가 들어갈 수 있다.

컴포넌트가 Actor와 같은 라이프사이클을 공유하지만 완전히 같은 타이밍은 아닐 수 있다.


Tick 구조

컴포넌트의 update는 TickComponent()에서 돈다.
이 Tick은 등록된 상태에서만 돌고, PrimaryComponentTick.bCanEverTick이 켜져 있어야 한다.

UMyMoveComponent::UMyMoveComponent()
{
    PrimaryComponentTick.bCanEverTick = true;
}

void UMyMoveComponent::TickComponent(
    float DeltaTime,
    ELevelTick TickType,
    FActorComponentTickFunction* ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}

Tick Group

Tick이 무조건 같은 시점에 도는 구조가 아니다.
Tick Group 개념이 따로 있고, physics 전후처럼 프레임 안의 어느 구간에서 업데이트할지를 지정할 수 있다.

  • 이동을 physics 전에 계산할지
  • 카메라를 physics 뒤에 따라오게 할지
  • UI나 effect를 어느 단계에서 갱신할지

컴포넌트 구조를 잘 나누는 것과 함께, 어떤 Tick Group에 둘지도 같이 봐야 한다.


컴포넌트 구조의 중요성

컴포넌트 구조는 재사용성과 역할 분리에 용이하다.
같은 기능을 여러 Actor에 붙이기 쉬워지고, 상속 깊이를 무한정 늘리지 않아도 된다.

기능을 컴포넌트로 나누면 필요한 Actor에만 붙이고, 다른 Actor에는 빼는 식으로 조립할 수 있다.

  • Pawn은 이동 컴포넌트를 가진다
  • Character는 캡슐, 메시, 무브먼트 컴포넌트를 조립한다
  • 커스텀 Actor도 루트, 메시, 충돌, 전용 로직 컴포넌트를 조립한다

Unreal의 Component 구조는 단순한 부속품 개념보다는, Actor를 기능 단위 객체들의 조합으로 만드는 방식에 가깝다.