AMyActor* NewActor = GetWorld()->SpawnActor<AMyActor>(
AMyActor::StaticClass(),
SpawnLocation,
SpawnRotation
);
SpwanActor로 생성하거나 에디터로 레벨에 배치해 놓았을 경우AMyActor::StaticClass()를 통해 리플렉션 결과물인 UClass객체를 가져옴StaticAllocateObject함수를 통해, 메모리 할당 받음UObject* NewObject = StaticAllocateObject(InClass, InOuter, InName, InFlags, ...);
InOuter를 해당 객체를 소유하는 상위객체로 설정(액터면 보통 월드나 레벨)
Outer Chain에 등록되어 GC에 의해 관리될 수 있음
AMyActor::AMyActor()
{
PrimaryActorTick.bCanEverTick = true;
// CreateDefaultSubobject는 생성자에서만 호출 가능!
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
MeshComp->SetupAttachment(RootComponent);
}
CreateDefaultSubobject를 통해 컴포넌트를 생성하고, CDO에 컴포넌트를 등록
CreateDefaultSubobject는 생성자에서만 호출 가능
객체가 생성되면, 생성자가 불려서 초기화 및 기본 컴포넌트 구조를 잡고, CDO에 저장된 값들로 덮어씌어져 초기화 됨
아직 월드에 올라오지 않고 메모리에만 존재하는 상태
void AMyActor::PostInitProperties()
{
Super::PostInitProperties();
}
모든 UPROPERTY가 초기화되고 난 상태
아직 월드에 없음
void UMyComponent::RegisterComponent()
{
Super::RegisterComponent();
}
이제야 컴포넌트가 월드에 등록됨
충돌 컴포넌트는 물리엔진에 등록
렌더링 컴포넌트는 Render Scene에 등록
Tick 함수 등록 등등
이 함수가 호출되지 않으면, 메모리에 컴포넌트는 존재하지만, 월드에는 존재하지 않게 됨 (아무 일도 안 함)
UStaticMeshComponent* NewMesh = NewObject<UStaticMeshComponent>(this);
NewMesh->SetStaticMesh(SomeMesh);
NewMesh->AttachToComponent(RootComponent,
FAttachmentTransformRules::KeepRelativeTransform);
NewMesh->RegisterComponent(); // 꼭 월드에 등록해주어야 잘 작동함
void AMyActor::PostActorCreated()
{
Super::PostActorCreated();
}
Actor 완전히 생성, 컴포넌트도 등록되고, 월드에도 존재하는 상태
하지만 아직 BeginPlay 전 상태로, 실행 상태는 아님
액터를 생성완료한 상태
void AMyActor::OnConstruction(const FTransform& Transform)
{
Super::OnConstruction(Transform);
}
생성자는 메모리 할당 직후에 딱 한 번 호출되는 것
이 함수는 컴포넌트 등록 후에 호출되고, 에디터에서 actor를 수정할 때마다 호출됨 (위치, 값 등등)
BeginPlay 전으로 불리는 마지막 함수
BeginPlay가 호출되려면, 해당 객체가 생성되고 등록이 완료되어 있어야 하며, World가 Play 상태여야 함
월드에 등록된 객체들을 돌며 BeginPlay를 호출하는데 그 순서는 보장되지 않음
레벨에 배치된 액터들은 이전의 생성과정까지 이루어졌다가, Play버튼을 누르면 BeginPlay부터 작동 시작
게임 중간에 SpawnActor로 객체 생성하면, 해당 객체를 먼저 생성부터 BeginPlay까지 한 번에 다 호출시키고 리턴받음
그래서 생성된 액터의 BeginPlay에서 또 다른 객체를 소환하게 되면, SpawnActor가 콜스택에 계속 쌓임
AMyActor* NewActor = GetWorld()->SpawnActor<AMyActor>(...);
// SpawnActor 내부에서 BeginPlay까지 호출되고 리턴
BeginPlay는 딱 한 번만 호출됨BeginPlay에선 객체들 다 존재함void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 이 시점에는 레벨의 모든 Actor가 생성 완료됨
TargetActor = UGameplayStatics::GetActorOfClass(GetWorld(),
ATargetActor::StaticClass());
}
BeginPlay는 무조건 호출됨AMyActor::AMyActor()
{
PrimaryActorTick.bCanEverTick = true;
}
RegisterComponent 하기
런타임 도중이라면
// Actor 레벨
SetActorTickEnabled(true);
SetActorTickEnabled(false);
// Component 레벨
MyComponent->SetComponentTickEnabled(true);
MyComponent->SetComponentTickEnabled(false);
FTickTaskManager가 틱 하는 애들 관리하며 돌려줌AMyActor::AMyActor()
{
PrimaryActorTick.bCanEverTick = true;
// Tick 그룹 설정
PrimaryActorTick.TickGroup = TG_PrePhysics;
}
TG_PrePhysics : 물리 시뮬레이션 전 캐릭터 이동
TG_DuringPhysics : 물리 시뮬레이션 중
TG_PostPhysics : 물리 시뮬레이션 후 물리 결과 반영 (카메라 이동)
PrimaryActorTick.AddPrerequisite(OtherActor, OtherActor->PrimaryActorTick);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();
}
Attach를 Register보다 먼저 해야함void AMyActor::RemoveDynamicMeshComponent()
{
if (DynamicMesh)
{
DynamicMesh->UnregisterComponent(); // 1. 월드에서 제거
DynamicMesh->DestroyComponent(); // 2. 메모리 해제
DynamicMesh = nullptr; // 3. 포인터 정리
}
}