我想用 commandlet 记录所有 actors,所以我写了这样的代码。
#include "MyCommandlet.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/StaticMeshActor.h"
#include "MyCharacter.h"
int32 UMyCommandlet::Main(const FString& Params)
{
TArray<AActor*> arrActors;
UWorld* World = GetWorld();
if (World)
{
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AMyCharacter::StaticClass(), arrActors);
for (int i = 0; i < arrActors.Num(); i++)
{
AMyCharacter* pCharacter = dynamic_cast<AMyCharacter*>(arrActors[i]);
UE_LOG(LogTemp, Display, TEXT("%d"), pCharacter->TestValue);
}
}
return 0;
}
但是 GetWorld() 返回 NULL。我认为原因是当调用 commandlet 时,没有加载任何级别。
实际上虚幻引擎文件说 “Commandlets 在“原始”环境中执行,在该环境中,游戏未加载,客户端代码未加载,关卡未加载,参与者也不存在。” 链接:https://docs.unrealengine.com/4.26/en-US/API/Runtime/Engine/Commandlets/UCommandlet/#:~:text=UCommandlet%20%3A%20public%20UObject-,Remarks,-Commandlet %20
那我怎样才能在 commandlet 中加载关卡或 getworld() ...??
试试这个:
FString levelAssetPath = TEXT("/Game/Maps/SomeLevel");
GEditor->GetEditorSubsystem<ULevelEditorSubsystem>()->LoadLevel(levelAssetPath);
也许你可以创建一个 FAutoConsoleCommandWithWorldAndArgs 来绑定你的函数,它会传入一个 UWorld。
FAutoConsoleCommandWithWorldAndArgs AbilitySystemToggleDebugHUDCommand(
TEXT("Your Command"),
TEXT("Your Help Text"),
FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(Your_Function)
);
static void Your_Function(const TArray<FString>& Args, UWorld* InWorld);
这个世界在 commandlet 中不应该为 NULL:
UWorld* World = GEditor->GetEditorWorldContext().World();
可以参考
UImportAssetsCommandlet::LoadLevel
中的Engine\Source\Editor\UnrealEd\Private\Commandlets\ImportAssetsCommandlet.cpp
手动加载umap并初始化UWorld:
bool UImportAssetsCommandlet::LoadLevel(const FString& LevelToLoad)
{
bool bResult = false;
if (!LevelToLoad.IsEmpty())
{
UE_LOG(LogAutomatedImport, Log, TEXT("Loading Map %s"), *LevelToLoad);
FString Filename;
if (FPackageName::TryConvertLongPackageNameToFilename(LevelToLoad, Filename))
{
UPackage* Package = LoadPackage(NULL, *Filename, 0);
UWorld* World = UWorld::FindWorldInPackage(Package);
if (World)
{
// Clean up any previous world. The world should have already been saved
UWorld* ExistingWorld = GEditor->GetEditorWorldContext().World();
GEngine->DestroyWorldContext(ExistingWorld);
ExistingWorld->DestroyWorld(true, World);
GWorld = World;
World->WorldType = EWorldType::Editor;
FWorldContext& WorldContext = GEngine->CreateNewWorldContext(World->WorldType);
WorldContext.SetCurrentWorld(World);
// add the world to the root set so that the garbage collection to delete replaced actors doesn't garbage collect the whole world
World->AddToRoot();
// initialize the levels in the world
World->InitWorld(UWorld::InitializationValues().AllowAudioPlayback(false));
World->GetWorldSettings()->PostEditChange();
World->UpdateWorldComponents(true, false);
bResult = true;
}
}
}
else
{
// a map was not specified, ignore
bResult = true;
}
if (!bResult)
{
UE_LOG(LogAutomatedImport, Error, TEXT("Could not find or load level %s"), *LevelToLoad);
}
return bResult;
}
然后使用
TActorIterator
登出Actor
if (World)
{
int32 ActorCount = 0;
for (TActorIterator<AActor> It(World); It; ++It)
{
AActor* CurrentActor = *It;
FString ActorName = CurrentActor->GetName();
UE_LOG(LogTemp, Log, TEXT("Actor: %s"), *ActorName);
ActorCount++;
}
UE_LOG(LogTemp, Log, TEXT("Total number of actors: %d"), ActorCount);
}
else
{
UE_LOG(LogTemp, Error, TEXT("Failed to load the specified map."));
}