BlueRoseNote/03-UnrealEngine/Editor/UnrealEngine的命令行操作方式.md
2023-06-29 11:55:02 +08:00

187 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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=<script_file>`比如:
```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 listAutomation 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<FString>& OutBeautifiedNames, TArray <FString>& 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<UBlueprint>(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}"
```