187 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
---
 | 
						||
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 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<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}"
 | 
						||
```
 |