--- title: UnrealEngine的命令行操作方式 date: 2023-04-27 11:15:14 excerpt: tags: CommandLet rating: ⭐ --- # 前言 最近想实现使用UnrealEditor-Cmd进行资产处理并且渲染的功能,这里简单归纳一下。 # 命令行启动方式 ## CommandLet 继承UCommandLet,重写Run()相关逻辑写在里面即可。启动参数大致如下: ```bash UnrealEditor-Cmd.exe ProjectPath -run=CommandletName ``` 这里很推荐去看一下`UImportAssetsCommandlet`,里面实现了使用Json传参的方法。 ### 变相执行ConsoleCommand 创建一个CommandLet并且接收参数,并在最后下列代码即可。 ```c++ GEditor->Exec(World, TEXT("MAP REBUILD ALLDIRTYFORLIGHTING")); ``` ## Python - 官方文档:https://docs.unrealengine.com/4.27/en-US/ProductionPipelines/ScriptingAndAutomation/Python/ ```bash UnrealEditor-Cmd.exe ProjectPath -ExecutePythonScript="c:\my_script.py" ``` 还存在另一种方法,编辑器启动时环境最小,不包含UI或渲染。该方法执行起来非常快,但是加载脚本需要交互的关卡和其他种类资源时比较棘手。在命令行中添加以下参数:-`run=pythonscript -script=`比如: ```bash UnrealEditor-Cmd.exe -run=pythonscript -script="c:\\my_script.py" ``` ## AutomationTest 一个另类的思路就是使用自动测试工具。通过`IMPLEMENT_COMPLEX_AUTOMATION_TEST`实现一个自动测试类之后实现`RunTest`即可。启动参数: ```bash UnrealEditor-Cmd.exe ProjectPath -AutomationTestName -Execcmds="Command-Line Arguments" ``` 大致过程可以参考视频:https://www.youtube.com/watch?v=kJd5-jY46Gk 视频中的启动参数: ```python import subprocess engine = "C:/work/Epic/UE_5.0/Engine/Binaries/win64/UnrealEditor.exe" project = "C:/Work/Prototypes/CompileBlueprintProj/CompileBlueprintProj.uproject" log_location = "C:/Work/Prototypes/CompileBlueprintProj/blueprint_results.log" cmd =[ engine,project, f"-abslog={flog_location}", "-editortest", "-Execcmds= "Automation SetFilter Stress, Automation list,Automation RunTest Project.Blueprints.compile Blueprints\"","-testexit=\ "Automation Test Queue Empty\"", cmd = " ".join(cmd) subprocess.run(cmd) ``` 引擎内的FCompileBlueprintsTest具体代码: ```c++ IMPLEMENT_COMPLEX_AUTOMATION_TEST(FCompileBlueprintsTest, "Project.Blueprints.Compile Blueprints", EAutomationTestFlags::EditorContext | EAutomationTestFlags::StressFilter) /************************************************************************/ /* FCompileBlueprintsTest */ /************************************************************************/ /** Requests a enumeration of all blueprints to be loaded */ void FCompileBlueprintsTest::GetTests(TArray& OutBeautifiedNames, TArray & OutTestCommands) const { FBlueprintAutomationTestUtilities::CollectTestsByClass(UBlueprint::StaticClass(), OutBeautifiedNames, OutTestCommands, /*bool bIgnoreLoaded =*/false); } bool FCompileBlueprintsTest::RunTest(const FString& Parameters) { UE_LOG(LogBlueprintAutomationTests, Log, TEXT("Beginning compile test for %s"), *Parameters); return FBlueprintAutomationTestUtilities::CompileBlueprint(Parameters); } /** * Simulates the user pressing the blueprint's compile button (will load the * blueprint first if it isn't already). ** @param BlueprintAssetPath The asset object path that you wish to compile. * @return False if we failed to load the blueprint, true otherwise */ static bool CompileBlueprint(const FString& BlueprintAssetPath) { UBlueprint* BlueprintObj = Cast(StaticLoadObject(UBlueprint::StaticClass(), NULL, *BlueprintAssetPath)); if (!BlueprintObj || !BlueprintObj->ParentClass) { UE_LOG(LogBlueprintAutomationTests, Error, TEXT("Failed to compile invalid blueprint, or blueprint parent no longer exists.")); return false; } UPackage* const BlueprintPackage = BlueprintObj->GetOutermost(); // compiling the blueprint will inherently dirty the package, but if there // weren't any changes to save before, there shouldn't be after bool const bStartedWithUnsavedChanges = (BlueprintPackage != nullptr) ? BlueprintPackage->IsDirty() : true; FKismetEditorUtilities::CompileBlueprint(BlueprintObj, EBlueprintCompileOptions::SkipGarbageCollection); if (BlueprintPackage != nullptr) { BlueprintPackage->SetDirtyFlag(bStartedWithUnsavedChanges); } return true; } ``` # 编辑器内调用 ## Python - 官方文档:https://docs.unrealengine.com/4.27/en-US/ProductionPipelines/ScriptingAndAutomation/Python/ 将OutputLog的ConsoleCommand的类型从CMD=>Python,之后输入Python命令即可。 ## CustomConsoleCommand 除了UFUNCTION中指定Exec之外(因为这个只能在部分类中实现并不通用)就是使用`IConsoleManager::Get().RegisterConsoleCommand()`,卸载函数为`IConsoleManager::Get().UnregisterConsoleObject()`,一般会在Module的StartupModule()/ShutdownModule()或者Subsystem的对应函数中进行注册/卸载。用法如下: ```c++ IConsoleManager::Get().RegisterConsoleCommand( TEXT("ConsoleCommandName"), TEXT("Useage Info"), FConsoleCommandDelegate::CreateStatic(&UYourClass::Function), ECVF_Default); ``` 相关的FConsoleCommandDelegate委托都位于IConsoleManager.h,根据需求选择。 # 其他 ## CommandLet Server ```c++ int32 FEditorDomainSaveServer::Run() { if (!TryInitialize()) { Shutdown(); return 1; } while (PollShouldRun()) { bool bIsIdle = true; TickPendingPackages(bIsIdle); PollIncomingConnections(bIsIdle); PollConnections(bIsIdle); TickMaintenance(bIsIdle); } Shutdown(); return 0; } void FEditorDomainSaveServer::TickMaintenance(bool bIsIdle) { using namespace UE::EditorDomainSave; SetIdle(bIsIdle); double CurrentTime = FPlatformTime::Seconds(); if (bIsIdle) { if (!HasExpectedConnections() && CurrentTime - IdleStartTime > Constants::ServerAbdicationCooldownSeconds) { if (TryAbdicate()) { return; } } } double CollectGarbageCooldownSeconds = bIsIdle ? Constants::CollectGarbageIdleCooldownSeconds : Constants::CollectGarbageActiveCooldownSeconds; bool bCollectedGarbageAfterIdle = bIsIdle && LastGarbageTime >= IdleStartTime; if (!bCollectedGarbageAfterIdle && CurrentTime - LastGarbageTime > CollectGarbageCooldownSeconds) { CollectGarbage(RF_NoFlags); LastGarbageTime = FPlatformTime::Seconds(); } if (bIsIdle) { FPlatformProcess::Sleep(Constants::ServerIdleSleepPeriodSeconds); } } ``` ## 日志写入文件 ```bash f"-abslog={log_location}" ```