본문 바로가기

UE4

[UE4] World 내 Object/Actor 순회

0. 서문

Object / Actor 순회는 개별 대응되는 iterator 클래스(ObjectIterator / ActorIterator)와 이에 대한 ranged-for-base 기능을 지원하기 위해 만들어진 wrapper 클래스(ObjectRange / ActorRange)를 사용하는 두 가지 방식으로 나뉠 수 있다.
 

1. ObjectIterator/ObjectRange

현재 월드에 로딩된 언리얼 오브젝트(UObject)를 순회하기 위해 사용한다.
 
모든 언리얼 오브젝트를 순회하는 FObjectIterator와 특정 타입의 언리얼 오브젝트를 순회하는 TObjectIterator/TObjectRange로 나눌 수 있다.
 
Iterator와 Range 클래스는 "Runtime/CoreUObject/Public/UObject/UObjectIterator.h" 파일에 정의되어 있다.
 

1) FObjectIterator

언리얼 오브젝트(UObject) 타입을 특정짓지 않고, 순회할 때 사용하는데, 이 녀석은 Range 클래스가 별도로 존재하지 않는 점은 참고/유의해야 한다.
아래 예제는 현재 월드에 로딩된 모든 언리얼 오브젝트(UObject)를 순회한다.
// World에 로딩된 모든 UObject 순회
for (FObjectIterator entity; entity; ++entity)
{
    UE_LOG(LogABGameInstance, Warning, TEXT("Loaded UObject : %s"), *entity->GetName());
}

 

위 코드를 샘플 프로젝트에 넣고 테스트 해보면, 왜 Range 클래스가 없는지 이해도 간다.
별 것 없는 샘플 프로젝트에서 위 코드를 실행했더니 44000개가 넘는 로그가 출력되었다.
 
아주 특수한 상황에서의 디버깅이 아니면 쓸 일이 거의 없을 듯 하다.
(도대체 답을 모를 때라던가... 상식적인 방법은 다 써봤다던가... 뭐 그런 상황...)
 

2) TObjectIterator/TObjectRange

FObjectIterator/FObjectRange와 달리 TObjectIterator/TObjectRange는 특정 타입의 언리얼 오브젝트를 순회할 수 있기에, 훨씬 더 실용성이 높다.
아래 예제는 현재 로딩된 UStaticMesh만 순회한다.
// 월드에 로딩된 UStaticMesh 오브젝트 순회
// 아래 2문장은 완전히 동일하다
for (TObjectIterator<UStaticMesh> entity; entity; ++entity)
for (const auto& entity : TObjectRange<UStaticMesh>())
{
    UE_LOG(LogABGameInstance, Warning, TEXT("Loaded UStaticMesh : %s"), *entity->GetName());
}
 

2. ActorIterator/ActorRange

Actor는 레벨에 배치된 UObject이므로 순회에 현재 UWorld 객체를 요구한다.
 
월드 내 레벨에 배치된 모든 Actor를 순회하는 FActorIterator/FActorRange와 특정 타입의 액터를 순회하는 TActorIterator/TActorRage로 나눌 수 있다.
 
Iterator와 Range 클래스는 "Runtime/Engine/Public/EngineUtils.h" 파일에 정의되어 있다.
 

1) FActorIterator/FActorRange 예제

ActorType을 특정하기 않고, 모든 Actor를 순회할 때 사용한다.
아래 예제는 월드 내 모든 Actor에 대해 각 Actor들이 어떤 DefaultSubobject들을 가지고 있는지 출력한다.
UWorld* world = GetWorld();

// World의 모든 Actor 순회
// 아래 2문장은 완전히 동일하다
for (FActorInterator(world) entity; entity; ++entity)
for (const auto& entity : FActorRange(world))
{
    UE_LOG(LogABGameInstance, Warning, TEXT("Actor : %s"), *(entity->GetName()));

    TArray<UObject*> components;

    // UObject::GetDefaultSubobjects (Runtime/CoreUObject/Private/UObject/Obj.cpp)
    // 해당 UObject의 defaultSubobject들을 out parameter인 TArray에 채워줌
    entity->GetDefaultSubobjects(components);
    for (const auto& entity : components)
    {
        UE_LOG(LogABGameInstance, Warning, TEXT("   -- Component : %s"), *(entity->GetClass()->GetName()));
    }
}
 

2) TActorIterator / TActorRange

FActorIterator/FActorRange와 달리 TActorIterator/TActorRange는 ActorType을 지정하여 순회할 수 있어, 개발 과정에서 더 자주 쓰이는 녀석이라 할 수 있다.
아래 예제는 레벨 내 Actor중 StaticMeshActor만 순회한다.
// World의 Actor중 StaticMeshActor 한정 순회
// 아래 2문장은 완전히 동일하다
for (TActorIterator<AStaticMeshActor> entity(world); entity; ++entity)
for (const auto& entity : TActorRange<AStaticMeshActor>(world))
{
    UE_LOG(LogABGameInstance, Warning, TEXT("StaticMeshActor : %s"), *(entity->GetName()));
}