UE C++ 如何获取特定类的实例

704 阅读2分钟

在 Unreal Engine 的蓝图中,可以通过 Get All Actors of Class来获取指定类的所有实例:

在 C++ 中应该如何获取呢?

AActor 实例数组

双击蓝图中的节点,跳转到工程项目的下面方法中:

UFUNCTION(BlueprintCallable, Category="Actor",  meta=(WorldContext="WorldContextObject", DeterminesOutputType="ActorClass", DynamicOutputParam="OutActors"))
static void GetAllActorsOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors);

所以我们可以通过下面的方法来获取到所有 AActor 实例的数组:

#include "Kismet/GameplayStatics.h"
 
TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass(), FoundActors);

AActor 子类实例数组

方法一:UGameplayStatics

在获取 C++ 定义的 AActor 子类时,将 GameplayStatics::GetAllActorsOfClas方法中的参数修改为子类的类型即可:

#include "Kismet/GameplayStatics.h"

TArray<AActor*> FoundActors; 
UGameplayStatics::GetAllActorsOfClass(GetWorld(), YourClass::StaticClass(), FoundActors); 

获取蓝图定义的 AActor 子类时,需要定义一个方法参数(或者类型属性) TSubclassOf<AActor> ClassToFind ,公开给蓝图使用,并在蓝图中进行设置。

#include "Kismet/GameplayStatics.h"

// Needs to be populated somehow (e.g. by exposing to blueprints as uproperty and setting it there 
TSubclassOf<AActor> ClassToFind;

TArray<AActor*> FoundActors; 
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ClassToFind, FoundActors);

注意,在 GetAllActorsOfClass 方法中,用于接收查询结果的数组类型依然为TArray<AActor*>,而不是对应的子类数组类型。那如何将TArray<AActor*>转换为子类数组呢?没有简单的方法,还需要 for 循环进行转换。

TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASomeActor::StaticClass(), FoundActors);

TArray<YourClass*> YourClassActors;
for (AActor* UncastedActor : FoundActors) {
    YourClassActors.Add(Cast<YourClass>(UncastedActor));
}

方法二:TActorIterator

另外还可以通过迭代器(TActorIterator)来获取 UWorld 中指定类型的实例:

#include "EngineUtils.h"

TArray<YourClass*> YourClassActors;

for (TActorIterator<YourClass> It(World); It; ++It)
{
    YourClassActors.Add(*It);
}

写成更通用的模板方法为:

#include "EngineUtils.h"

template <typename ActorClass>
void TGetAllActorsOfClass(UWorld* World, TArray<ActorClass*>& OutActors)
{
    OutActors.Reset();

    if (World)
    {
        for (TActorIterator<ActorClass> It(World); It; ++It)
        {
            OutActors.Add(*It);
        }
    }
}

示例如下:

void Test(UWorld* World)
{
    TArray<APawn*> MyPawns;
    TGetAllActorsOfClass(World, MyPawns);

    TArray<ACharacter*> MyCharacters;
    TGetAllActorsOfClass(World, MyCharacters);

    /*
    Won't compile because UObject not a sub-class of AActor.
    TArray<UObject*> MyObjects;
    TGetAllActorsOfClass(World, MyObjects);
    */
}

注意:

在使用TActorIterator<ActorClass> It(World)时,需要包含头文件 #include "EngineUtils.h",否则会报错 无法从“UWorld *”转换为“int”

UObjects 的所有实例

如果需要获取某个 UObjects 类的所有实例(不仅仅是 Actors),可以通过下面的方法:

for (TObjectIterator<UserClassObject> It; It; ++It)
{
    UMyObject* CurrentObject = *It;
    UE_LOG(LogTemp, Log, TEXT("Found UObject named:%s"), *CurrentObject.GetName());
}

另外也可以使用AssetRegistryModule

template<typename T>
void GetObjectsOfClass(TArray<T*>& OutArray)
{
    FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
    TArray<FAssetData> AssetData;
    AssetRegistryModule.Get().GetAssetsByClass(T::StaticClass()->GetFName(), AssetData);
    for (int i = 0; i < AssetData.Num(); i++) {
        T* Object = Cast<T>(AssetData[i].GetAsset());
        OutArray.Add(Object);
    }
}

示例如下:

TArray<UAnimSequence*> AnimSequences;
GetObjectsOfClass(AnimSequences);

参考