UE5 Actor & Component 생명주기
개요 — 생명주기를 알아야 하는 이유
Section titled “개요 — 생명주기를 알아야 하는 이유”UE5에서 Actor와 Component 초기화 순서를 잘못 이해하면 NullPointerException, 초기화 시점 오류, 메모리 릭이 발생합니다. 각 함수가 어느 시점에 호출되는지, 어디서 무엇을 초기화해야 하는지 명확히 이해하는 것이 안정적인 코드의 기초입니다.
1. Actor 생명주기 전체 흐름
Section titled “1. Actor 생명주기 전체 흐름”[에디터/스폰] │ ▼ UObject::UObject() │ (메모리 할당, 기본 초기화) ▼ AActor::AActor() ← 생성자 │ (컴포넌트 생성, 기본값 설정) ▼ PostLoad() / PostActorCreated() │ (에디터 로드 or 런타임 스폰 직후) ▼ PostInitializeComponents() │ (모든 컴포넌트 RegisterComponent 완료 후) ▼ PreInitializeComponents() ← [내부 호출 순서상 위치 다름, 참고용] │ ▼ BeginPlay() │ (게임 시작 시점 — 레벨 시작 or 스폰 후) ▼ Tick(float DeltaTime) ← 매 프레임 (활성화된 경우) │ ▼ [Actor 제거 요청: Destroy() / 레벨 언로드] │ ▼ EndPlay(EEndPlayReason) │ (리소스 해제, 구독 해제) ▼ BeginDestroy() │ (GC 수거 시작) ▼ FinishDestroy() │ (완전 소멸) ▼ [메모리 해제]2. 생성자 — AActor::AActor()
Section titled “2. 생성자 — AActor::AActor()”생성자는 에디터에서도 호출됩니다. 월드 접근이나 GetWorld()는 사용할 수 없습니다.
AMyCharacter::AMyCharacter(){ // Tick 활성화 여부 설정 PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.bStartWithTickEnabled = false; // 기본 비활성화
// 컴포넌트 생성 (반드시 생성자에서) HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComponent")); WeaponComponent = CreateDefaultSubobject<UWeaponComponent>(TEXT("WeaponComponent"));
// SceneComponent 계층 설정 RootComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleComponent")); MeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("MeshComponent")); MeshComponent->SetupAttachment(RootComponent);
// UPROPERTY 기본값 설정 MaxHealth = 100.f; MoveSpeed = 400.f;
// !! 금지: GetWorld(), GetController(), 다른 Actor 접근}생성자에서 해야 할 것:
CreateDefaultSubobject<T>()— 컴포넌트 생성SetupAttachment()— 컴포넌트 계층- 기본값 설정 (UPROPERTY 변수)
PrimaryActorTick설정
생성자에서 하면 안 되는 것:
GetWorld()— nullptrGetController()— nullptr- 다른 Actor 참조
3. PostInitializeComponents
Section titled “3. PostInitializeComponents”모든 컴포넌트가 등록(Register)된 후 호출됩니다. 컴포넌트 간 상호참조를 설정하기에 적합합니다.
void AMyCharacter::PostInitializeComponents(){ Super::PostInitializeComponents(); // 반드시 먼저 호출
// 이 시점에 모든 컴포넌트가 초기화됨 if (HealthComponent) { // 컴포넌트 간 델리게이트 연결 HealthComponent->OnDeath.AddUObject(this, &AMyCharacter::HandleDeath); }
// 메시 컴포넌트 초기화 작업 if (MeshComponent) { MeshComponent->SetAnimationMode(EAnimationMode::AnimationBlueprint); }
// 아직 GetWorld()는 신뢰할 수 없는 타이밍도 있음 (에디터 환경)}4. BeginPlay — 게임 시작 시점
Section titled “4. BeginPlay — 게임 시작 시점”레벨 시작 시 또는 런타임 스폰 후 첫 Tick 전에 한 번 호출됩니다. 게임 로직 초기화의 주된 위치입니다.
void AMyCharacter::BeginPlay(){ Super::BeginPlay(); // 반드시 먼저 호출
// GetWorld() 안전 사용 가능 CurrentHealth = MaxHealth;
// 컨트롤러 접근 가능 if (APlayerController* PC = Cast<APlayerController>(GetController())) { SetupEnhancedInput(PC); }
// 서브시스템 접근 if (UWaveManagerSubsystem* WaveMgr = GetWorld()->GetSubsystem<UWaveManagerSubsystem>()) { WaveMgr->OnWaveStarted.AddUObject(this, &AMyCharacter::HandleWaveStarted); }
// Tick 활성화 (BeginPlay에서 필요할 때 켜기) SetActorTickEnabled(true);
UE_LOG(LogTemp, Log, TEXT("%s BeginPlay"), *GetName());}5. Tick — 매 프레임 갱신
Section titled “5. Tick — 매 프레임 갱신”void AMyCharacter::Tick(float DeltaTime){ Super::Tick(DeltaTime); // 반드시 호출
// DeltaTime: 이전 프레임과의 시간 간격 (초) // 프레임 독립적 계산에 반드시 사용 RegenerationTimer += DeltaTime; if (RegenerationTimer >= RegenerationInterval) { RegenerationTimer = 0.f; Heal(RegenerationAmount); }}
// Tick 비활성화 — 불필요한 Tick을 끄면 성능 향상void AMyCharacter::HandleDeath(){ SetActorTickEnabled(false);}Tick 그룹 설정
Section titled “Tick 그룹 설정”AMyCharacter::AMyCharacter(){ PrimaryActorTick.bCanEverTick = true; // TG_PrePhysics (기본), TG_DuringPhysics, TG_PostPhysics, TG_PostUpdateWork PrimaryActorTick.TickGroup = TG_PostPhysics; // 물리 연산 후 실행}6. EndPlay — 정리 시점
Section titled “6. EndPlay — 정리 시점”Actor가 월드에서 제거되기 직전에 호출됩니다. 구독 해제, 타이머 정리, 리소스 해제를 여기서 합니다.
void AMyCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason){ // 타이머 정리 GetWorldTimerManager().ClearAllTimersForObject(this);
// 델리게이트 구독 해제 if (UWaveManagerSubsystem* WaveMgr = GetWorld()->GetSubsystem<UWaveManagerSubsystem>()) { WaveMgr->OnWaveStarted.RemoveAll(this); }
// 백그라운드 작업 정리 if (FileLoader) { FileLoader->EnsureCompletion(); FileLoader.Reset(); }
// 이유별 처리 switch (EndPlayReason) { case EEndPlayReason::Destroyed: UE_LOG(LogTemp, Log, TEXT("%s was destroyed"), *GetName()); break; case EEndPlayReason::LevelTransition: SaveState(); // 레벨 전환 시 상태 저장 break; case EEndPlayReason::EndPlayInEditor: break; default: break; }
Super::EndPlay(EndPlayReason); // 마지막에 호출}EEndPlayReason 종류
Section titled “EEndPlayReason 종류”| 이유 | 설명 |
|---|---|
Destroyed | Destroy() 호출 |
LevelTransition | 레벨 전환 |
EndPlayInEditor | PIE 종료 |
RemovedFromWorld | 월드에서 제거 |
Quit | 게임 종료 |
7. Component 생명주기
Section titled “7. Component 생명주기”UActorComponent::UActorComponent() ← 컴포넌트 생성자 │ ▼OnRegister() ← 월드에 등록 (에디터 포함) │ ▼InitializeComponent() ← bWantsInitializeComponent = true 시 │ ▼BeginPlay() ← Actor의 BeginPlay 내에서 │ ▼TickComponent() ← 매 프레임 │ ▼EndPlay() ← Actor EndPlay 내에서 │ ▼OnUnregister() ← 월드에서 해제 │ ▼BeginDestroy() / FinishDestroy()// Component 초기화 예시void UHealthComponent::InitializeComponent(){ Super::InitializeComponent(); CurrentHealth = MaxHealth;}
void UHealthComponent::BeginPlay(){ Super::BeginPlay();
// Owner Actor 접근 가능 OwnerActor = GetOwner();
// Owner의 다른 컴포넌트 접근 AnimComponent = OwnerActor->FindComponentByClass<UAnimMontageComponent>();}8. 초기화 위치 결정 가이드
Section titled “8. 초기화 위치 결정 가이드”| 작업 | 권장 위치 |
|---|---|
컴포넌트 생성 (CreateDefaultSubobject) | 생성자 |
| 기본값 설정 | 생성자 |
| 컴포넌트 간 델리게이트 연결 | PostInitializeComponents |
| 게임 로직 초기화 | BeginPlay |
| GetWorld() 사용 | BeginPlay 이후 |
| 타이머 설정 | BeginPlay |
| 구독 해제·타이머 정리 | EndPlay |
| 매 프레임 계산 | Tick |
9. 스폰 시 생명주기 순서
Section titled “9. 스폰 시 생명주기 순서”// SpawnActor 호출 시 순서// 1. 생성자 호출// 2. PostSpawnInitialize// 3. PostActorCreated (런타임 스폰)// 4. ExecuteConstruction (Construction Script)// 5. PostInitializeComponents// 6. BeginPlay (레벨 시작 또는 SpawnActor 완료 후)
FActorSpawnParameters Params;Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;AMyActor* Actor = GetWorld()->SpawnActor<AMyActor>(AMyActor::StaticClass(), SpawnLocation, SpawnRotation, Params);// 이 시점에 BeginPlay까지 완료됨10. 정리
Section titled “10. 정리”- 생성자: 컴포넌트 생성·기본값 설정만. GetWorld() 금지
- PostInitializeComponents: 컴포넌트 간 참조·델리게이트 연결
- BeginPlay: 게임 로직 초기화, 타이머, 서브시스템 접근
- Tick: 매 프레임 로직. 불필요하면 비활성화
- EndPlay: 구독 해제, 타이머 정리, 리소스 반환
Super::함수명()을 빠뜨리면 엔진 기본 동작이 누락되어 예측 불가능한 버그 발생