Init
This commit is contained in:
32
03-UnrealEngine/Editor/UE消息对话框使用.md
Normal file
32
03-UnrealEngine/Editor/UE消息对话框使用.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: UE消息对话框使用
|
||||
date: 2022-09-28 14:41:08
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
|
||||
```c++
|
||||
void FSimpleEditorsModule::PluginButtonClicked()
|
||||
{
|
||||
//消息框显示的消息内容
|
||||
FText DialogText = FText::Format(
|
||||
LOCTEXT("PluginButtonDialogText", "Add code to {0} in {1} to override this button's actions"),
|
||||
FText::FromString(TEXT("FSimpleEditorsModule::PluginButtonClicked()")),
|
||||
FText::FromString(TEXT("SimpleEditors.cpp"))
|
||||
);
|
||||
EAppReturnType::Type ReturnType = FMessageDialog::Open(EAppMsgType::OkCancel, DialogText);
|
||||
if (ReturnType == EAppReturnType::Type::Ok)
|
||||
{
|
||||
//消息框OK按钮被点击执行
|
||||
UE_LOG(LogTemp,Log,TEXT("Click OK Button."))
|
||||
FMessageDialog::ShowLastError(); //弹出默认的系统原生消息对话框
|
||||
}
|
||||
else
|
||||
{
|
||||
//消息框Cancel按钮被点击执行
|
||||
UE_LOG(LogTemp, Log, TEXT("Click Cancel Button."))
|
||||
FMessageDialog::Debugf(DialogText); //弹出默认的OK消息对话框
|
||||
}
|
||||
}
|
||||
```
|
168
03-UnrealEngine/Editor/UTexture2D的读取与写入数据并且保存成Asset.md
Normal file
168
03-UnrealEngine/Editor/UTexture2D的读取与写入数据并且保存成Asset.md
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
title: UTexture2D的读取与写入数据并且保存成Asset
|
||||
date: 2022-11-04 10:11:39
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
## UTexture2D的读取与写入数据
|
||||
在阅读@YivanLee的文章
|
||||
https://zhuanlan.zhihu.com/p/45171840
|
||||
|
||||
我发现“**向UTexture2D写入数据**”这节有些纰漏:
|
||||
1. 作者没有说明Texture2D->PlatformData->Mips[0].BulkData.LockReadOnly()返回空指针的详细情况。
|
||||
2. 作者实现的写入是临时性的。
|
||||
|
||||
在查阅国外资料时,我在isaratech的网站里找到解决方案,在此我将进行简单说明。(原文地址在结尾会介绍)
|
||||
|
||||
## 从UTexture2D读取数据
|
||||
读取方法如下:
|
||||
```
|
||||
const FColor* FormatedImageData = static_cast<const FColor*>( MyTexture2D->PlatformData->Mips[0].BulkData.LockReadOnly());
|
||||
for(int32 X = 0; X < MyTexture2D->SizeX; X++)
|
||||
{
|
||||
for (int32 Y = 0; Y < MyTexture2D->SizeY; Y++)
|
||||
{
|
||||
FColor PixelColor = FormatedImageData[Y * MyTexture2D->SizeX + X];
|
||||
// Do the job with the pixel
|
||||
}
|
||||
}
|
||||
|
||||
MyTexture2D->PlatformData->Mips[0].BulkData.Unlock();
|
||||
```
|
||||
|
||||
但有的时候LockReadOnly()会返回空指针,这个时候你需要去检查UTexture2D的设置:
|
||||
1. CompressionSettings 是否为VectorDisplacementmap。
|
||||
2. MipGenSettings 是否为NoMipmaps。
|
||||
3. SRGB 是否为未勾选状态。
|
||||
|
||||
但通常情况下都不可能同时符合这3个条件,所以我们需要将UTexture2D的3个选项设置成上述这3个状态。在处理所需操作后再进行恢复。代码如下:
|
||||
```
|
||||
TextureCompressionSettings OldCompressionSettings = MyTexture2D->CompressionSettings; TextureMipGenSettings OldMipGenSettings = MyTexture2D->MipGenSettings; bool OldSRGB = MyTexture2D->SRGB;
|
||||
|
||||
MyTexture2D->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap;
|
||||
MyTexture2D->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
|
||||
MyTexture2D->SRGB = false;
|
||||
MyTexture2D->UpdateResource();
|
||||
|
||||
const FColor* FormatedImageData = static_cast<const FColor*>( MyTexture2D->PlatformData->Mips[0].BulkData.LockReadOnly());
|
||||
for(int32 X = 0; X < MyTexture2D->SizeX; X++)
|
||||
{
|
||||
for (int32 Y = 0; Y < MyTexture2D->SizeY; Y++)
|
||||
{
|
||||
FColor PixelColor = FormatedImageData[Y * MyTexture2D->SizeX + X];
|
||||
//做若干操作
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MyTexture2D->PlatformData->Mips[0].BulkData.Unlock();
|
||||
Texture->CompressionSettings = OldCompressionSettings;
|
||||
Texture->MipGenSettings = OldMipGenSettings;
|
||||
Texture->SRGB = OldSRGB;
|
||||
Texture->UpdateResource();
|
||||
```
|
||||
|
||||
## 向UTexture2D写入数据
|
||||
写入操作也是类似,不过我们需要调用若干UPackage与FAssetRegistryModule的代码。
|
||||
下文中的Asset相关操作需要在包含头文件:
|
||||
># include "AssetRegistryModule.h"
|
||||
|
||||
同时需要在项目模块c#文件的PublicDependencyModuleNames.AddRange中加入"AssetTools"。
|
||||
### 取得Package的指针
|
||||
这里可以使用CreatePackage来创建新的包或是调用FindPackage来取得已有包的UPackage指针。包这个概念来源于UE3与UDK时代,当时引擎所有的资源都存放在一个一个的包中,修改过资源后还需要选中并且点击保存包选项,才能真正保存,但是UE4淡化了这个概念。
|
||||
|
||||
你可以把包理解为Content下各个文件夹。
|
||||
```
|
||||
//创建名为PackageName值的包
|
||||
//PackageName为FString类型
|
||||
FString AssetPath = TEXT("/Game/")+ PackageName+ TEXT("/");
|
||||
AssetPath += TextureName;
|
||||
UPackage* Package = CreatePackage(NULL, *AssetPath);
|
||||
Package->FullyLoad();
|
||||
```
|
||||
|
||||
FindPackage本人还没用过,不过用法应该差不多。
|
||||
### 取得UTexture2D的指针
|
||||
你可以创建一个新的UTexture2D,也可以通过蓝图指定。
|
||||
|
||||
```
|
||||
//创建
|
||||
UTexture2D* NewTexture = NewObject<UTexture2D>(Package, *TextureName, RF_Public | RF_Standalone | RF_MarkAsRootSet);
|
||||
NewTexture->AddToRoot();
|
||||
NewTexture->PlatformData = new FTexturePlatformData();
|
||||
NewTexture->PlatformData->SizeX = TextureWidth;
|
||||
NewTexture->PlatformData->SizeY = TextureHeight;
|
||||
NewTexture->PlatformData->NumSlices = 1;
|
||||
//设置像素格式
|
||||
NewTexture->PlatformData->PixelFormat = EPixelFormat::PF_B8G8R8A8;
|
||||
```
|
||||
### 写入数据
|
||||
```
|
||||
//创建一个uint8的数组并取得指针
|
||||
//这里需要考虑之前设置的像素格式
|
||||
uint8* Pixels = new uint8[TextureWidth * TextureHeight * 4];
|
||||
for (int32 y = 0; y < TextureHeight; y++)
|
||||
{
|
||||
for (int32 x = 0; x < TextureWidth; x++)
|
||||
{
|
||||
int32 curPixelIndex = ((y * TextureWidth) + x);
|
||||
//这里可以设置4个通道的值
|
||||
//这里需要考虑像素格式,之前设置了PF_B8G8R8A8,那么这里通道的顺序就是BGRA
|
||||
Pixels[4 * curPixelIndex] = 100;
|
||||
Pixels[4 * curPixelIndex + 1] = 50;
|
||||
Pixels[4 * curPixelIndex + 2] = 20;
|
||||
Pixels[4 * curPixelIndex + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
//创建第一个MipMap
|
||||
FTexture2DMipMap* Mip = new FTexture2DMipMap();
|
||||
NewTexture->PlatformData->Mips.Add(Mip);
|
||||
Mip->SizeX = TextureWidth;
|
||||
Mip->SizeY = TextureHeight;
|
||||
|
||||
//锁定Texture让它可以被修改
|
||||
Mip->BulkData.Lock(LOCK_READ_WRITE);
|
||||
uint8* TextureData = (uint8*)Mip->BulkData.Realloc(TextureWidth * TextureHeight * 4);
|
||||
FMemory::Memcpy(TextureData, Pixels, sizeof(uint8) * TextureHeight * TextureWidth * 4);
|
||||
Mip->BulkData.Unlock();
|
||||
|
||||
//通过以上步骤,我们完成数据的临时写入
|
||||
//执行完以下这两个步骤,编辑器中的asset会显示可以保存的状态(如果是指定Asset来获取UTexture2D的指针的情况下)
|
||||
NewTexture->Source.Init(TextureWidth, TextureHeight, 1, 1, ETextureSourceFormat::TSF_BGRA8, Pixels);
|
||||
NewTexture->UpdateResource();
|
||||
```
|
||||
|
||||
### 创建Asset并清理无用数据
|
||||
```
|
||||
Package->MarkPackageDirty();
|
||||
FAssetRegistryModule::AssetCreated(NewTexture);
|
||||
//通过asset路径获取包中文件名
|
||||
FString PackageFileName = FPackageName::LongPackageNameToFilename(AssetPath, FPackageName::GetAssetPackageExtension());
|
||||
//进行保存
|
||||
bool bSaved = UPackage::SavePackage(Package, NewTexture, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone, *PackageFileName, GError, nullptr, true, true, SAVE_NoError);
|
||||
|
||||
delete[] Pixels;
|
||||
```
|
||||
|
||||
之后你就可以在编辑器中找到新生成的Asset。如果你是向选中的Asset写入数据(执行到上一步),调用UPackage::SavePackage即可进行保存。
|
||||
|
||||
## 总结
|
||||
|
||||
本文包含以下知识:
|
||||
1. UTexture2D的部分结构、属性以及读取设置方法。
|
||||
2. UPackage部分的部分操作。
|
||||
3. Asset的创建操作。
|
||||
|
||||
读者可以执行通过阅读UPackage、FAssetRegistryModule的代码学习Asset更多的Asset操作。
|
||||
|
||||
## 个人推荐的isaratech网站文章
|
||||
网站
|
||||
https://isaratech.com/
|
||||
|
||||
生成一个新的贴图Asset保存
|
||||
https://isaratech.com/save-a-procedurally-generated-texture-as-a-new-asset/
|
||||
|
||||
生成一个新的带有节点的MaterialAsset
|
||||
https://isaratech.com/ue4-programmatically-create-a-new-material-and-inner-nodes/
|
186
03-UnrealEngine/Editor/UnrealEngine的命令行操作方式.md
Normal file
186
03-UnrealEngine/Editor/UnrealEngine的命令行操作方式.md
Normal file
@@ -0,0 +1,186 @@
|
||||
---
|
||||
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}"
|
||||
```
|
Reference in New Issue
Block a user