TIL_102 : 액터(컴포넌트) 생성, BeginPlay, Tick

김펭귄·2026년 1월 26일

Today What I Learned (TIL)

목록 보기
102/139

1. 액터 생성시의 과정

AMyActor* NewActor = GetWorld()->SpawnActor<AMyActor>(
    AMyActor::StaticClass(),
    SpawnLocation,
    SpawnRotation
);
  • SpwanActor로 생성하거나 에디터로 레벨에 배치해 놓았을 경우

1.1. UClass 가져오기

  • AMyActor::StaticClass()를 통해 리플렉션 결과물인 UClass객체를 가져옴

1.2. 메모리 할당

  • StaticAllocateObject함수를 통해, 메모리 할당 받음
UObject* NewObject = StaticAllocateObject(InClass, InOuter, InName, InFlags, ...);
  • InOuter를 해당 객체를 소유하는 상위객체로 설정(액터면 보통 월드나 레벨)

  • Outer Chain에 등록되어 GC에 의해 관리될 수 있음

1.3. 생성자 호출

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

    // CreateDefaultSubobject는 생성자에서만 호출 가능!
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
    MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    MeshComp->SetupAttachment(RootComponent);
}
  • CreateDefaultSubobject를 통해 컴포넌트를 생성하고, CDO에 컴포넌트를 등록

  • CreateDefaultSubobject는 생성자에서만 호출 가능

  • 객체가 생성되면, 생성자가 불려서 초기화 및 기본 컴포넌트 구조를 잡고, CDO에 저장된 값들로 덮어씌어져 초기화 됨

  • 아직 월드에 올라오지 않고 메모리에만 존재하는 상태

1.4. PostInitProperties

void AMyActor::PostInitProperties()
{
    Super::PostInitProperties();
}
  • 모든 UPROPERTY가 초기화되고 난 상태

  • 아직 월드에 없음

1.5. PreRegisterAllComponents

  • 컴포넌트를 월드에 등록하기 전에 준비하는 상태

1.6. RegisterComponent

void UMyComponent::RegisterComponent()
{
	Super::RegisterComponent();
}
  • 이제야 컴포넌트가 월드에 등록됨

    • 충돌 컴포넌트는 물리엔진에 등록

    • 렌더링 컴포넌트는 Render Scene에 등록

    • Tick 함수 등록 등등

  • 이 함수가 호출되지 않으면, 메모리에 컴포넌트는 존재하지만, 월드에는 존재하지 않게 됨 (아무 일도 안 함)

UStaticMeshComponent* NewMesh = NewObject<UStaticMeshComponent>(this);
NewMesh->SetStaticMesh(SomeMesh);
NewMesh->AttachToComponent(RootComponent, 
						   FAttachmentTransformRules::KeepRelativeTransform);
NewMesh->RegisterComponent();  // 꼭 월드에 등록해주어야 잘 작동함

1.7. PostActorCreated

void AMyActor::PostActorCreated()
{
    Super::PostActorCreated();
}
  • Actor 완전히 생성, 컴포넌트도 등록되고, 월드에도 존재하는 상태

  • 하지만 아직 BeginPlay 전 상태로, 실행 상태는 아님

  • 액터를 생성완료한 상태

1.8. OnConstruction

void AMyActor::OnConstruction(const FTransform& Transform)
{
    Super::OnConstruction(Transform);
}
  • 생성자는 메모리 할당 직후에 딱 한 번 호출되는 것

  • 이 함수는 컴포넌트 등록 후에 호출되고, 에디터에서 actor를 수정할 때마다 호출됨 (위치, 값 등등)

  • BeginPlay 전으로 불리는 마지막 함수

2. BeginPlay

  • BeginPlay가 호출되려면, 해당 객체가 생성되고 등록이 완료되어 있어야 하며, World가 Play 상태여야 함

  • 월드에 등록된 객체들을 돌며 BeginPlay를 호출하는데 그 순서는 보장되지 않음

  • 레벨에 배치된 액터들은 이전의 생성과정까지 이루어졌다가, Play버튼을 누르면 BeginPlay부터 작동 시작

  • 게임 중간에 SpawnActor로 객체 생성하면, 해당 객체를 먼저 생성부터 BeginPlay까지 한 번에 다 호출시키고 리턴받음

  • 그래서 생성된 액터의 BeginPlay에서 또 다른 객체를 소환하게 되면, SpawnActor가 콜스택에 계속 쌓임

AMyActor* NewActor = GetWorld()->SpawnActor<AMyActor>(...);
// SpawnActor 내부에서 BeginPlay까지 호출되고 리턴
  • BeginPlay는 딱 한 번만 호출됨

2.1. BeginPlay에서 초기화해야하는 이유

  1. 생성자에선 아직 월드에 존재 안 할 수 있지만, BeginPlay에선 객체들 다 존재함
void AMyActor::BeginPlay()
{
    Super::BeginPlay();
    // 이 시점에는 레벨의 모든 Actor가 생성 완료됨
    TargetActor = UGameplayStatics::GetActorOfClass(GetWorld(),
    												ATargetActor::StaticClass());
}
  1. 레벨 재시작 시, 생성자는 호출 안 될수도 있지만, BeginPlay는 무조건 호출됨

3. Tick

3.1. Tick 사용하려면

  • 생성자에서 틱 허용
AMyActor::AMyActor()
{
    PrimaryActorTick.bCanEverTick = true;  
}
  • RegisterComponent 하기

  • 런타임 도중이라면

// Actor 레벨
SetActorTickEnabled(true);
SetActorTickEnabled(false);

// Component 레벨
MyComponent->SetComponentTickEnabled(true);
MyComponent->SetComponentTickEnabled(false);
  • 그러면 FTickTaskManager가 틱 하는 애들 관리하며 돌려줌

3.2. Tick Group

  • 틱도 도는 순서가 존재 (캐릭터 이동이 먼저 일어나고, 카메라가 따라 이동해야함)
AMyActor::AMyActor()
{
    PrimaryActorTick.bCanEverTick = true;

    // Tick 그룹 설정
    PrimaryActorTick.TickGroup = TG_PrePhysics;
}
  • TG_PrePhysics : 물리 시뮬레이션 전 캐릭터 이동

  • TG_DuringPhysics : 물리 시뮬레이션 중

  • TG_PostPhysics : 물리 시뮬레이션 후 물리 결과 반영 (카메라 이동)

  • PrimaryActorTick.AddPrerequisite(OtherActor, OtherActor->PrimaryActorTick);
    • 의존성 가지고 특정 Actor 다음에 Tick돌게 할 수도 있음

4. 컴포넌트 동적 추가/제거

4.1. 컴포넌트 동적 추가

void AMyActor::AddDynamicMeshComponent()
{
    // 1. NewObject로 생성
    UStaticMeshComponent* Mesh = NewObject<UStaticMeshComponent>(this, TEXT("Mesh"));

    // 2. 설정
    NewMesh->SetStaticMesh(SomeMesh);
    NewMesh->SetRelativeLocation(FVector(100, 0, 0));

    // 3. Attach (Register 전에!)
    NewMesh->AttachToComponent(RootComponent, 
    						   FAttachmentTransformRules::KeepRelativeTransform);

    // 4. Register 필수!
    NewMesh->RegisterComponent();
}
  • AttachRegister보다 먼저 해야함

4.2. 컴포넌트 동적 제거

void AMyActor::RemoveDynamicMeshComponent()
{
    if (DynamicMesh)
    {
        DynamicMesh->UnregisterComponent();  // 1. 월드에서 제거
        DynamicMesh->DestroyComponent();     // 2. 메모리 해제
        DynamicMesh = nullptr;               // 3. 포인터 정리
    }
}
profile
반갑습니다

0개의 댓글