油管uepython教程

在UE中的python初始配置

可以看看这个文章https://www.yumefx.com/?p=3211

插件的选择

找到scripting启用这三个
image.png

项目设置

image.png
第一个 startup scripts 中填写的是,当启动UE时自动加载的脚本的路径
第二个Additional paths 中填写的是 当在UE中使用import模块时 import的额外搜索路径

使用python导入资产

导入贴图和音频

用到的类:
https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/AssetImportTask.html
https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/AssetTools.html
首先先了解一下引擎中的文件路径:这里内容的路径为/Game,因此假如要在内容下创建一个新的Tex文件夹,那么Tex文件夹的路径就是/Game/Tex
image.png
在ue的编辑界面中按shitf+回车可以输入多行代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import unreal

soundPath = 'D:/ZhangRuiChen/UETest/TestFile/Sounds/soundTest.mp3'
texPath = 'D:/ZhangRuiChen/UETest/TestFile/Texture/test_ORM3.png'


def importMyAssets(): # 创建导入任务并执行导入
texture_task = buildImportTask(texPath, '/Game/Textures') # 通过buildImportTask函数创建一个AssetImportTask对象
sound_task = buildImportTask(soundPath, '/Game/Sounds') # 同理
executeImportTasks([texture_task, sound_task]) # 执行导入操作


# 这里用到的属性可以参考文档:
def buildImportTask(filename, destination_path): # 构建导入任务
task = unreal.AssetImportTask()
task.set_editor_property('automated', True) # 设置为True后就不会弹出对话框,就是将其设置为自动化
task.set_editor_property('destination_name', '') # 可选的要导入的自定义名称,当属性为空时就按照文件名称命名
task.set_editor_property('destination_path', destination_path) # 导入的资源在引擎中的路径
task.set_editor_property('filename', filename) # 要导入的资源在电脑磁盘上的路径
task.set_editor_property('replace_existing', True) # 是否要覆盖资产
task.set_editor_property('save', True) # 导入后保存
return task


# 这里用到的属性可以参考文档:https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/AssetTools.html
def executeImportTasks(tasks): # 执行导入任务
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(tasks) # 通过这行代码将在buildImportTask函数中创建的task对象进行导入
for task in tasks:
for path in task.get_editor_property('imported_object_paths'):
print('Imported: %s' % path)

导入静态网格体和骨骼网格体

用到的类:
https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/FbxImportUI.html
https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/FbxMeshImportData.html
https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/FbxStaticMeshImportData.html
https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/FbxSkeletalMeshImportData.html
我用的4.26版本的UE,使用这个代码没有办法导入骨骼体,代码已经反复检查几遍了没发现问题。教程中用的是4.21版本的UE。 后来测试后发现是可以的,应该是之前的资产有问题,后来测试用的小白人fbx2018的是可以导入进来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import unreal

soundPath = 'D:/ZhangRuiChen/UETest/TestFile/Sounds/soundTest.mp3'
texPath = 'D:/ZhangRuiChen/UETest/TestFile/Texture/test_ORM3.png'
static_mesh_fbx = 'D:/ZhangRuiChen/UETest/TestFile/StaticMeshes/sphere.fbx'
skeletal_mesh_fbx = 'D:/ZhangRuiChen/UETest/TestFile/Skeletal/woman.fbx'

def importMyAssets(): # 创建导入任务并执行导入
# texture_task = buildImportTask(texPath, '/Game/Textures') # 通过buildImportTask函数创建一个AssetImportTask对象
# sound_task = buildImportTask(soundPath, '/Game/Sounds') # 同理
# executeImportTasks([texture_task, sound_task]) # 执行导入操作
static_mesh_task = buildImportTask(static_mesh_fbx, '/Game/StaticMeshes', buildStaticMeshImportOptions())
skeletal_mesh_task = buildImportTask(skeletal_mesh_fbx, '/Game/SkeletalMeshes', buildSkeletalMeshImportOptions())
executeImportTasks([static_mesh_task, skeletal_mesh_task])


# 这里用到的属性可以参考文档:https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/AssetImportTask.html
def buildImportTask(filename, destination_path, options=None): # 构建导入任务
task = unreal.AssetImportTask()
task.set_editor_property('automated', True) # 设置为True后就不会弹出对话框,就是将其设置为自动化
task.set_editor_property('destination_name', '') # 可选的要导入的自定义名称,当属性为空时就按照文件名称命名
task.set_editor_property('destination_path', destination_path) # 导入的资源在引擎中的路径
task.set_editor_property('filename', filename) # 要导入的资源在电脑磁盘上的路径
task.set_editor_property('replace_existing', True) # 是否要覆盖资产
task.set_editor_property('save', True) # 导入后保存
task.set_editor_property('options', options) # 当导入fbx这种资产需要额外的导入选项,需要创建FbxImportUI对象来传递
return task


# 这里用到的属性可以参考文档:https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/AssetTools.html
def executeImportTasks(tasks): # 执行导入任务
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(tasks) # 通过这行代码将在buildImportTask函数中创建的task对象进行导入
for task in tasks:
for path in task.get_editor_property('imported_object_paths'):
print('Imported: %s' % path)


def buildStaticMeshImportOptions():
options = unreal.FbxImportUI()
# unreal.FbxImportUI
options.set_editor_property('import_mesh', True)
options.set_editor_property('import_textures', False)
options.set_editor_property('import_materials', False)
options.set_editor_property('import_as_skeletal', False) # Static Mesh
# unreal.FbxMeshImportData
options.static_mesh_import_data.set_editor_property('import_translation', unreal.Vector(0.0, 0.0, 0.0))
options.static_mesh_import_data.set_editor_property('import_rotation', unreal.Rotator(0.0, 0.0, 0.0))
options.static_mesh_import_data.set_editor_property('import_uniform_scale', 1.0)
# unreal.FbxStaticMeshImportData
options.static_mesh_import_data.set_editor_property('combine_meshes', True)
options.static_mesh_import_data.set_editor_property('generate_lightmap_u_vs', True)
options.static_mesh_import_data.set_editor_property('auto_generate_collision', True)
return options


def buildSkeletalMeshImportOptions():
options = unreal.FbxImportUI()
# unreal.FbxImportUI
options.set_editor_property('import_mesh', True)
options.set_editor_property('import_textures', True)
options.set_editor_property('import_materials', True)
options.set_editor_property('import_as_skeletal', True) # Skeletal Mesh
# unreal.FbxMeshImportData
options.skeletal_mesh_import_data.set_editor_property('import_translation', unreal.Vector(0.0, 0.0, 0.0))
options.skeletal_mesh_import_data.set_editor_property('import_rotation', unreal.Rotator(0.0, 0.0, 0.0))
options.skeletal_mesh_import_data.set_editor_property('import_uniform_scale', 1.0)
# unreal.FbxSkeletalMeshImportData
options.skeletal_mesh_import_data.set_editor_property('import_morph_targets', True)
options.skeletal_mesh_import_data.set_editor_property('update_skeleton_reference_pose', False)
return options

导入动画

跟导入静态网格体差不多,就是导入动画时的额外设置略有不同,并且创建任务需要使用导入动画的设置时需要指定动画依赖的骨骼的路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33


def buildAnimationImportOptions(skeleton_path):
options = unreal.FbxImportUI()

# 是否导入动画
options.set_editor_property('import_animations', True)
# 导入骨架的位置
options.skeleton = unreal.load_asset(skeleton_path)

# 设置动画序列的内容
options.anim_sequence_import_data.set_editor_property('import_translation', unreal.Vector(0.0, 0.0, 0.0))
options.anim_sequence_import_data.set_editor_property('import_rotation', unreal.Rotator(0.0, 0.0, 0.0))
options.anim_sequence_import_data.set_editor_property('import_uniform_scale', 1.0)

# 动画的长度
options.anim_sequence_import_data.set_editor_property('animation_length',
unreal.FBXAnimationLengthImportType.FBXALIT_EXPORTED_TIME)
# 去掉冗余的关键帧
options.anim_sequence_import_data.set_editor_property('remove_redundant_keys', False)
return options

def executeImportTasks(tasks): # 执行导入任务
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(tasks) # 通过这行代码将在buildImportTask函数中创建的task对象进行导入
for task in tasks:
for path in task.get_editor_property('imported_object_paths'):
print('Imported: %s' % path)

def importMyAssets(): # 创建导入任务并执行导入
animation_fbx = 'D:/ZhangRuiChen/UETest/TestFile/Animation/Animation.fbx'
animation_fbx_task = buildImportTask(animation_fbx, '/Game/Animations', buildStaticMeshImportOptions('/Game/SkeletalMeshes/man_Skeleton'))
executeImportTasks([animation_fbx_task])

创建、复制、删除、重命名资产和文件夹

这些东西可以直接复制到UE的代码编辑框中执行看看效果
针对资产编辑器中的内容都要使用unreal.EditorAssetLibrary中的方法,例如创建复制删除重命名文件夹,复制删除重命名资产,判段资产是否存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import unreal
# 创建文件夹
def createDirectory():
unreal.EditorAssetLibrary.make_directory('/Game/MyAsset/MyNewDirectory')


# 复制文件夹
def duplicateDirectory():
return unreal.EditorAssetLibrary.duplicate_directory('/Game/MyAsset/MyNewDirectory',
'/Game/MyAsset/MyNewDirectory_Duplicated')


# 删除文件夹
def deleteDirectory():
unreal.EditorAssetLibrary.delete_directory('/Game/MyAsset/MyNewDirectory')


# 重命名文件夹
def renameDirectory():
return unreal.EditorAssetLibrary.rename_directory('/Game/MyAsset/MyNewDirectory_Duplicated',
'/Game/MyAsset/MyNewDirectory_Renamed')


# 复制资产
def duplicateAsset():
return unreal.EditorAssetLibrary.duplicate_asset('/Game/MyAsset/Textures/test_ORM3',
'/Game/MyAsset/Textures/test_ORM3_duplicate')


# 删除资产
def deleteAsset():
unreal.EditorAssetLibrary.delete_asset('/Game/MyAsset/Textures/test_ORM3')


# 判断资产是否存在
def assetExist():
print(unreal.EditorAssetLibrary.does_asset_exist('/Game/MyAsset/Textures/test_ORM3')) # False
print(unreal.EditorAssetLibrary.does_asset_exist('/Game/MyAsset/Textures/test_ORM3_duplicate')) # True


# 重命名资产
def renameAsset():
unreal.EditorAssetLibrary.rename_asset('/Game/MyAsset/Textures/test_ORM3_duplicate',
'/Game/MyAsset/Textures/test_ORM3_Renamed')


# 显示复制资产提示框
def duplicateAssetDialog(show_dialog=True):
if show_dialog:
unreal.AssetToolsHelpers.get_asset_tools().duplicate_asset_with_dialog('test_ORM3_Duplicated',
'/Game/MyAsset/Textures',
unreal.load_asset(
'/Game/MyAsset/Textures/test_ORM3_Renamed'))
else:
unreal.AssetToolsHelpers.get_asset_tools().duplicate_asset('test_ORM3_Duplicated', '/Game/MyAsset/Textures',
unreal.load_asset(
'/Game/MyAsset/Textures/test_ORM3_Renamed'))


# 显示重命名提示框
def renameAssetDialog(show_dialog=True):
first_rename_data = unreal.AssetRenameData(unreal.load_asset('/Game/MyAsset/Textures/test_ORM3_Renamed'),
'/Game/MyAsset/Textures', 'test_ORM3_Renamed_2')
second_rename_data = unreal.AssetRenameData(unreal.load_asset('/Game/MyAsset/Textures/test_ORM3_Duplicated'),
'/Game/MyAsset/Textures', 'test_ORM3_Duplicated_Renamed')
if show_dialog:
unreal.AssetToolsHelpers.get_asset_tools().rename_assets_with_dialog([first_rename_data, second_rename_data])
else:
unreal.AssetToolsHelpers.get_asset_tools().rename_assets([first_rename_data, second_rename_data])

使用python调用C++函数

image.png
取名字为ZFunctionsimage.png
创建这个类以后,UE会自动编译打开VisualStudio。image.png
ZFunctions.h文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "ZFunctions.generated.h"

/**
*
*/
UCLASS()
class CPLUSPLUSTEST_API UZFunctions : public UBlueprintFunctionLibrary
{
GENERATED_BODY()

public:
UFUNCTION(BlueprintCallable) // 将函数暴露给蓝图
static void CalledFromPython(FString InputString);

};

ZFunctions.cpp文件内容:

1
2
3
4
5
6
7
// Fill out your copyright notice in the Description page of Project Settings.

#include "ZFunctions.h"

void UZFunctions::CalledFromPython(FString InputString) {
UE_LOG(LogTemp, Error, TEXT("%s"), *InputString);
}

内容写好后就可以
然后我们可以在编辑器中通过

1
2
for x in sorted(dir(unreal)):
print (x)

打印出目前UE项目中所有的类。
可以在输出中找到image.png那就可以使用了。
然后我们可以在编辑器中通过

1
2
for x in sorted(dir(unreal.ZFunctions)):
print (x)

打印出ZFunctions中的所有方法。
可以找到刚才定义的方法:image.png 可以发现我们明明写的是CalledFromPython,但是UE会自动将命名更改为called_from_python
然后我们就可以在UE中使用这个方法了
通过代码 unreal.ZFunctions.called_from_python(‘11111’)
会使用这个方法的功能,让UE打印一个UE_LOG
UE_LOG(LogTemp, Error, TEXT(“%s”), *InputString)
image.png

改变文件夹的颜色

要想修改文件夹的颜色依然需要使用C++,只不过可以通过python语句调用C++函数来实现简单的批量操作…
因此依然通过继承Blueprint Fuction Library 类来创建我们的自定义类。取名为CppLib

CppLib.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CppLib.generated.h"

/**
*
*/
UCLASS()
class CPLUSPLUSTEST_API UCppLib : public UBlueprintFunctionLibrary
{
GENERATED_BODY()

public:
UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void SetFolderColor(FString FolderPath, FLinearColor Color);
};

CppLib.cpp

1
2
3
4
5
6
7
8
// Fill out your copyright notice in the Description page of Project Settings.


#include "CppLib.h"

void UCppLib::SetFolderColor(FString FolderPath, FLinearColor Color) {
GConfig->SetString(TEXT("PathColor"), *FolderPath, *Color.ToString(), GEditorPerProjectIni);
}

写完c++后可以通过蓝图调用函数

image.png
勾选自定义事件的 Call In Editor 就可以在外面手动点击触发自定义事件了。image.png如果是已经存在的文件夹路径,那么点击按钮后不会立刻改变文件夹的颜色,需要重新启动才能够发现改变。不存在的文件夹路径,当创建好对应的文件夹后,文件夹会自动改变颜色。

python调用函数实现批量更改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# coding: utf-8

import unreal


# import folderColor as fc
# from imp import reload
# reload(fc)
# fc.generateColoredDirectories()

def generateColoredDirectories():
for x in range(100, 400):
dir_path = '/Game/PythonGenerated/' + str(x)
linear_color = getGradientColor(x)
unreal.CppLib.set_folder_color(dir_path, linear_color)
unreal.EditorAssetLibrary.make_directory(dir_path)


def getGradientColor(x):
x = x - 100
if x < 100:
r = 1.0 - x / 100.0
g = 0.0 + x / 100.0
b = 0.0
elif x < 200:
r = 0.0
g = 1.0 - (x - 100) / 100.0
b = 0.0 + (x - 100) / 100.0
else:
r = 0.0 + (x - 200) / 100.0
g = 0.0
b = 1.0 - (x - 200) / 100.0
return unreal.LinearColor(r, g, b, 1)

效果:image.png

如何使用c++和Python打开和关闭资产

python 只能打开资产,关闭资产需要通过C++来实现。

先看python如何打开资产

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import unreal
# 在ue中执行的调用命令
# from imp import reload
# import openAssets_10 as oa
# reload(oa)
# oa.openAssets()

# 打开资产
def openAssets():
assets = [unreal.load_asset('/Game/MyAsset/Sounds/soundTest'),
unreal.load_asset('/Game/MyAsset/Textures/light_setAOVs'),
unreal.load_asset('/Game/MyAsset/Textures/light_createShot'),
unreal.load_asset('/Game/MyAsset/Textures/light_importLight')]
unreal.AssetToolsHelpers.get_asset_tools().open_editor_for_assets(assets)

# 调用自定义的C++类中的函数
def getAllOpenedAssets():
return unreal.CppLib.get_assets_opened_in_editor()

def closeAssets():
assets = getAllOpenedAssets()
unreal.CppLib.close_editor_for_assets(assets)

UE中模块修改

image.png这个.Build.cs格式的是当前项目的模块
进入这个模块需要修改:
主要是增加了一项:“UnrealEd”

CPlusPlusTest.Build.cs

首先说明一下这里的CPlusPlusTest是ue4.27中创建的项目名
在原有的基础上新增了"UnrealEd"
image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class CPlusPlusTest : ModuleRules
{
public CPlusPlusTest(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "UnrealEd" });
}
}

CppLib.h

在上一节的基础上 新增了两个函数:
CloseEditorForAssets
GetAssetsOpendInEditor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CppLib.generated.h"

/**
*
*/
UCLASS()
class CPLUSPLUSTEST_API UCppLib : public UBlueprintFunctionLibrary
{
GENERATED_BODY()

public:
UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void SetFolderColor(FString FolderPath, FLinearColor Color);

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void CloseEditorForAssets(TArray<UObject*> Assets);

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static TArray<UObject*> GetAssetsOpenedInEditor();
};

CppLib.cpp

这里除了定义函数功能还有个要注意的是要include “Editor.h”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Fill out your copyright notice in the Description page of Project Settings.


#include "CppLib.h"
#include "Editor.h"
#include "Editor/UnrealEd/Public/Toolkits/AssetEditorManager.h"
void UCppLib::SetFolderColor(FString FolderPath, FLinearColor Color) {
GConfig->SetString(TEXT("PathColor"), *FolderPath, *Color.ToString(), GEditorPerProjectIni);
}

void UCppLib::CloseEditorForAssets(TArray<UObject*> Assets) {
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
for (UObject* Asset : Assets) {
AssetEditorSubsystem->CloseAllEditorsForAsset(Asset);
}
}

TArray<UObject*> UCppLib::GetAssetsOpenedInEditor() {
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
TArray<UObject*> EditedAssets = AssetEditorSubsystem->GetAllEditedAssets();
return EditedAssets;
}

在UE中进行调用

from imp import reload
import openAssets_10 as oa
reload(oa)
oa.openAssets()# 打开资产
oa.closeAssets()# 关闭资产
oa.getAllOpenedAssets()#得到当前打开的资产的信息(需要用一个变量来接受它,是一个列表)

使用Python和C ++选择内容浏览器的资产

python

使用python来选择有弊端,就是它不能选择文件夹以及关卡,因此还要介绍通过C++如何选择

1
2
3
4
5
6
7
8
9
import unreal
# from imp import reload
# import selectAssets_11 as sa
# reload(sa)
# sa.showAssetsInContentBrowser()
def showAssetsInContentBrowser():
paths = ['/Game/MyAsset/Sounds/SoundTest',
'/Game/MyAsset/Textures/light_setAOVs']
unreal.EditorAssetLibrary.sync_browser_to_objects(paths)

CPlusPlusTest.Build.cs

又新增了这三个,因为.cpp用到了Moduleimage.png 最后的EditorWidgets不添加的话编译不了,教程中没有加这个,UE4.27,VS2019需要加EditorWidgets,加了以后就可以用了
image.png

CppLib.h

到现在我才发现在头文件里面写了构造函数后可以通过左边的螺丝刀图标快速创建在源文件中image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CppLib.generated.h"

/**
*
*/
UCLASS()
class CPLUSPLUSTEST_API UCppLib : public UBlueprintFunctionLibrary
{
GENERATED_BODY()

public:
UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void SetFolderColor(FString FolderPath, FLinearColor Color); // 批量创建文件夹并修改颜色

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void CloseEditorForAssets(TArray<UObject*> Assets); // 关闭打开的资产的窗口

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static TArray<UObject*> GetAssetsOpenedInEditor(); // 获得已经打开资产窗口的资产的路径字符串列表

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static TArray<FString> GetSelectedAssets(); // 获得选择的资产的路径字符串列表

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static TArray<FString> GetSelectedFolders(); // 获得选择的文件夹的路径字符串列表

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void SetSelectedAssets(TArray<FString> Paths); // 选择路径字符串列表中的资产

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void SetSelectedFolders(TArray<FString> Paths); // 选择路径字符串列表中的文件夹

};

CppLib.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// Fill out your copyright notice in the Description page of Project Settings.


#include "CppLib.h"
#include "Editor.h"
#include "Editor/UnrealEd/Public/Toolkits/AssetEditorManager.h"
#include "Editor/ContentBrowser/Public/ContentBrowserModule.h"
#include "Editor/ContentBrowser/Private/ScontentBrowser.h"
#include "Runtime/AssetRegistry/Public/AssetRegistryModule.h"
void UCppLib::SetFolderColor(FString FolderPath, FLinearColor Color) {
GConfig->SetString(TEXT("PathColor"), *FolderPath, *Color.ToString(), GEditorPerProjectIni);
}

void UCppLib::CloseEditorForAssets(TArray<UObject*> Assets) {
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
for (UObject* Asset : Assets) {
AssetEditorSubsystem->CloseAllEditorsForAsset(Asset);
}
}

TArray<UObject*> UCppLib::GetAssetsOpenedInEditor() {
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
TArray<UObject*> EditedAssets = AssetEditorSubsystem->GetAllEditedAssets();
return EditedAssets;
}

TArray<FString> UCppLib::GetSelectedAssets()
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
TArray<FAssetData> SelectedAssets;
ContentBrowserModule.Get().GetSelectedAssets(SelectedAssets);
TArray<FString> Result;
for (FAssetData& AssetData : SelectedAssets) {
Result.Add(AssetData.PackageName.ToString());
}
return Result;
}

TArray<FString> UCppLib::GetSelectedFolders()
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
TArray<FString> SelectedFolders;
ContentBrowserModule.Get().GetSelectedFolders(SelectedFolders);
return SelectedFolders;
}

void UCppLib::SetSelectedAssets(TArray<FString> Paths)
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
TArray<FName> PathsName;
for (FString Path : Paths) {
PathsName.Add(*Path);
}
FARFilter AssetFilter;
AssetFilter.PackageNames = PathsName;
TArray<FAssetData> AssetDatas;
AssetRegistryModule.Get().GetAssets(AssetFilter, AssetDatas);
ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas);
}

void UCppLib::SetSelectedFolders(TArray<FString> Paths)
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
TArray<FString> SelectedFolders;
ContentBrowserModule.Get().SyncBrowserToFolders(Paths);
}

如何用Python显示缓慢的任务进度

执行循环的过程的同时显示进度:
image.png

python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import unreal
import random
# from imp import reload
# import showSlowTasksProgression_12 as test
# reload(test)
# test.executeSlowTask()

def executeSlowTask():
quantity_steps_in_slow_task = 10000
with unreal.ScopedSlowTask(quantity_steps_in_slow_task, 'My Slow Task Text ...') as slow_task:
slow_task.make_dialog(True)
for x in range(quantity_steps_in_slow_task):
if slow_task.should_cancel():
break
slow_task.enter_progress_frame(1, 'My Slow Task Text ...' + str(x) + '/' + str(quantity_steps_in_slow_task))
# Execute slow logic
deferredSpawnActor()

def deferredSpawnActor():
world = unreal.EditorLevelLibrary.get_editor_world()
actor_class = unreal.EditorAssetLibrary.load_blueprint_class('/Game/MyBluePrint/BPActor')
actor_location = unreal.Vector(random.uniform(0.0, 2000.0), random.uniform(0.0, 2000.0), 0.0)
actor_rotation = unreal.Rotator(random.uniform(0.0, 360.0), random.uniform(0.0, 360.0), random.uniform(0.0, 360.0))
actor_scale = unreal.Vector(random.uniform(0.1, 2.0), random.uniform(0.1, 2.0), random.uniform(0.1, 2.0))
actor_transform = unreal.Transform(actor_location, actor_rotation, actor_scale)
# actor = unreal.GameplayStatics.begin_deferred_actor_spawn_from_class(world, actor_class, actor_transform)
# unreal.GameplayStatics.finish_spawning_actor(actor, actor_transform)
actor = unreal.CppLib.get_actor(world, actor_class, actor_transform)
unreal.CppLib.set_actor(actor,actor_transform)

CppLib.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CppLib.generated.h"

/**
*
*/
UCLASS()
class CPLUSPLUSTEST_API UCppLib : public UBlueprintFunctionLibrary
{
GENERATED_BODY()

public:
UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void SetFolderColor(FString FolderPath, FLinearColor Color); // 批量创建文件夹并修改颜色

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void CloseEditorForAssets(TArray<UObject*> Assets); // 关闭打开的资产的窗口

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static TArray<UObject*> GetAssetsOpenedInEditor(); // 获得已经打开资产窗口的资产的路径字符串列表

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static TArray<FString> GetSelectedAssets(); // 获得选择的资产的路径字符串列表

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static TArray<FString> GetSelectedFolders(); // 获得选择的文件夹的路径字符串列表

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void SetSelectedAssets(TArray<FString> Paths); // 选择路径字符串列表中的资产

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void SetSelectedFolders(TArray<FString> Paths); // 选择路径字符串列表中的文件夹

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static AActor* GetActor(const UObject* WorldContextObject,TSubclassOf< AActor > ActorClass,const FTransform& SpawnTransform); // 获取ActorBP

UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void setActor(class AActor* Actor,const FTransform& SpawnTransform); // 创建AcotrBP

};

CppLib.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Fill out your copyright notice in the Description page of Project Settings.


#include "CppLib.h"
#include "Editor.h"
#include "Editor/UnrealEd/Public/Toolkits/AssetEditorManager.h"
#include "Editor/ContentBrowser/Public/ContentBrowserModule.h"
#include "Editor/ContentBrowser/Private/ScontentBrowser.h"
#include "Runtime/AssetRegistry/Public/AssetRegistryModule.h"
#include "Runtime/Engine/Classes/Kismet/GameplayStatics.h"
void UCppLib::SetFolderColor(FString FolderPath, FLinearColor Color) {
GConfig->SetString(TEXT("PathColor"), *FolderPath, *Color.ToString(), GEditorPerProjectIni);
}

void UCppLib::CloseEditorForAssets(TArray<UObject*> Assets) {
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
for (UObject* Asset : Assets) {
AssetEditorSubsystem->CloseAllEditorsForAsset(Asset);
}
}

TArray<UObject*> UCppLib::GetAssetsOpenedInEditor() {
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
TArray<UObject*> EditedAssets = AssetEditorSubsystem->GetAllEditedAssets();
return EditedAssets;
}

TArray<FString> UCppLib::GetSelectedAssets()
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
TArray<FAssetData> SelectedAssets;
ContentBrowserModule.Get().GetSelectedAssets(SelectedAssets);
TArray<FString> Result;
for (FAssetData& AssetData : SelectedAssets) {
Result.Add(AssetData.PackageName.ToString());
}
return Result;
}

TArray<FString> UCppLib::GetSelectedFolders()
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
TArray<FString> SelectedFolders;
ContentBrowserModule.Get().GetSelectedFolders(SelectedFolders);
return SelectedFolders;
}

void UCppLib::SetSelectedAssets(TArray<FString> Paths)
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
TArray<FName> PathsName;
for (FString Path : Paths) {
PathsName.Add(*Path);
}
FARFilter AssetFilter;
AssetFilter.PackageNames = PathsName;
TArray<FAssetData> AssetDatas;
AssetRegistryModule.Get().GetAssets(AssetFilter, AssetDatas);
ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas);
}

void UCppLib::SetSelectedFolders(TArray<FString> Paths)
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
TArray<FString> SelectedFolders;
ContentBrowserModule.Get().SyncBrowserToFolders(Paths);
}

AActor* UCppLib::GetActor(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, const FTransform& SpawnTransform)
{
return UGameplayStatics::BeginDeferredActorSpawnFromClass(WorldContextObject, ActorClass, SpawnTransform);
}

void UCppLib::setActor(AActor* Actor, const FTransform& SpawnTransform)
{
UGameplayStatics::FinishSpawningActor(Actor, SpawnTransform);
}

打印类的所有属性

Cpplib.h

1
2
UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static TArray<FString>GetAllProperties(UClass* Class); //获取类的所有属性

Cpplib.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
TArray<FString> UCppLib::GetAllProperties(UClass* Class)
{
TArray<FString> Ret;
if (Class != nullptr) {
for (TFieldIterator<UProperty> It(Class); It; ++It) {
UProperty* Property = *It;
if (Property->HasAnyPropertyFlags(EPropertyFlags::CPF_Edit)) {
Ret.Add(Property->GetName());
}
}
}
return Ret;
}

python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import unreal

def getAllProperties(object_class):
return unreal.CppLib.get_all_properties(object_class) # 通过自定义C++的类中的函数,得到所有属性的字符串列表

def printAllProperties():
obj = unreal.Actor() # 将打印actor类型的所有属性
object_class = obj.get_class()
for x in getAllProperties(object_class):
y = x
# while循环的作用是为了对齐字符串
while len(y) < 42:
y = ' ' + y
print(y + ':' + str(obj.get_editor_property(x)))

执行效果

image.png

一次性执行多个cmd命令

CppLib.h

1
2
UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void ExecuteConsoleCommand(FString ConsoleCmd); //执行多行命令行

CppLib.cpp

需要#include “Editor.h”

1
2
3
4
5
6
7
8
9
void UCppLib::ExecuteConsoleCommand(FString ConsoleCmd)
{
if (GEditor) {
UWorld* World = GEditor->GetEditorWorldContext().World();
if (World) {
GEditor->Exec(World, *ConsoleCmd, *GLog);
}
}
}

python

1
2
3
4
5
6
7
8
9
10
11
import unreal
import random


def executeConsoleCommand():
console_cmd = ['r.ScreenPercentage 0.1',
'r.Color.Max 6',
'stat fps',
'stat unit']
for x in console_cmd:
unreal.CppLib.execute_console_command(x)

选择场景中的actor与清除选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import unreal


def get_selected_actors():
return unreal.EditorLevelLibrary.get_selected_level_actors()


def get_selected_actors_EXAMPLE():
for x in get_selected_actors():
print(x)


def select_actors(actors_to_select=[]):
unreal.EditorLevelLibrary.set_selected_level_actors(actors_to_select)


def select_actors_EXAMPLE():
all_actors = unreal.EditorLevelLibrary.get_all_level_actors()
actors_to_select = []
for x in range(len(all_actors)):
actors_to_select.append(all_actors[x])
select_actors(actors_to_select)


def clear_actor_selection_EXAMPLE():
select_actors()

生成默认资源(材质粒子关卡等)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 创建基础资产
def create_generic_asset(asset_path='', unique_name=True, asset_class=None, asset_factory=None):
if unique_name: # 如果命名冲突的话会自动生成一个新的唯一的路径名字(后缀加数字)
asset_path, asset_name = unreal.AssetToolsHelpers.get_asset_tools().create_unique_asset_name(
base_package_name=asset_path, suffix='')
if not unreal.EditorAssetLibrary.does_asset_exist(asset_path=asset_path):
path = asset_path.rsplit('/', 1)[0]
name = asset_path.rsplit('/', 1)[1]
return unreal.AssetToolsHelpers.get_asset_tools().create_asset(
asset_name=name,
package_path=path,
asset_class=asset_class,
factory=asset_factory
)
return unreal.load_asset(asset_path)


# 调用创建基础资产的举例
def create_generic_asset_EXAMPLE():
base_path = '/Game/GenericAssets/'
generic_assets = [
[
base_path + 'sequence',
unreal.LevelSequence,
unreal.LevelSequenceFactoryNew()
],
[
base_path + 'material',
unreal.Material,
unreal.MaterialFactoryNew()
],
[
base_path + 'world',
unreal.World,
unreal.WorldFactory()
],
[
base_path + 'particle_system',
unreal.ParticleSystem,
unreal.ParticleSystemFactoryNew()
],
[
base_path + 'paper_flipbook',
unreal.PaperFlipbook,
unreal.PaperFlipbookFactory()
]
]
for asset in generic_assets:
print(create_generic_asset(asset[0], True, asset[1], asset[2]))

在蓝图中使用python

在4.25版本之前的ue中需要用c++添加蓝图节点但是在新版本已经内置了能在蓝图中使用python的节点。但是无法在运行时可用的蓝图类(例如直接从 Actor 派生的类)中使用此方法。
image.png

B站搬运Udemy教程

链接:https://www.bilibili.com/video/BV1GY4y1s7qM?p=8&spm_id_from=pageDriver&vd_source=b1de3fe38e887eb40fc55a5485724480
https://www.bilibili.com/video/BV18q4y1X7DH?p=11&vd_source=b1de3fe38e887eb40fc55a5485724480
这两个教程链接内容是一样的,但是有的教程链接中的内容可能有点损坏,互补一下。
image.png

P10工厂和资产创造

什么是工厂:当我们需要创建一个资产的时候(蓝图,声音,关卡等),我们就需要去工厂去取(factory),因此我们需要先找到对应的工厂:LevelFactoryBlueprintFactory等。
以创建蓝图举例:
创建工厂对象相当于点击这些:image.png

创建完工厂后(点击后)需要选择继承的类image.png
初始设置完成后开始创造资产,创造资产需要使用unreal.AssetToolsHelpers.get_asset_tools()
利用这个工具中的create_asset方法创建。
创建完后用unreal.EditorAssetLibrary.save_loaded_asset进行保存
代码:

1
2
3
4
5
6
7
8
9
10
11
12
import unreal

blueprintName = "python_create_BP"
blueprintPath = "/Game/MyBluePrint"

factory = unreal.BlueprintFactory()
factory.set_editor_property("ParentClass", unreal.Actor)

assetTools = unreal.AssetToolsHelpers.get_asset_tools()
myFancyNewAssetFile = assetTools.create_asset(blueprintName, blueprintPath, None, factory)

unreal.EditorAssetLibrary.save_loaded_asset(myFancyNewAssetFile)

P11制作任务进度条:

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
import unreal

totalFrames = 1000000
textDisplay = "i love python, an i guess i'll be using this for a while!"

with unreal.ScopedSlowTask(totalFrames, textDisplay) as ST:
ST.make_dialog(True)
for i in range(totalFrames):
if ST.should_cancel():
break
unreal.log("one step!!!!")
ST.enter_progress_frame(1)

P12综合案例:批量创建继承自actor的蓝图资产(带进度条)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import unreal

totalRequiredBlueprints = 20
newAssetName = "BP_pythonMade_%d"
createdAssetsPath = "/Game/TestStuff"
slowTaskDisplayText = "Createing new assets....."

factory = unreal.BlueprintFactory()
factory.set_editor_property("ParentClass", unreal.Actor)

assetTools = unreal.AssetToolsHelpers.get_asset_tools()

with unreal.ScopedSlowTask(totalRequiredBlueprints, slowTaskDisplayText) as ST:
ST.make_dialog(True)
for x in range(totalRequiredBlueprints):
if ST.should_cancel():
break
newAsset = assetTools.create_asset(newAssetName % (x), createdAssetsPath, None, factory)
unreal.EditorAssetLibrary.save_loaded_asset(newAsset)
unreal.log("Just created an asset BP_PythonMade_%d via PYTHON API" % (x))
ST.enter_progress_frame(1)

P13 根据选择的资产创建Actor(在关卡中的都叫actor)

教程上的获取选择的资产是这样:
@unreal.uclass()
class MyEditorUtility(unreal.GlobalEditorUtilityBase):
pass

selectedAssets = MyEditorUtility().get_selected_assets()
但是官网教程有教可以通过这样获取选择的资产:
selectedAssets = unreal.EditorUtilityLibrary.get_selected_assets()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import unreal

actorsCount = 50
slowTaskDisplayText = "Spawning actors in the level....."

selectedAssets = unreal.EditorUtilityLibrary.get_selected_assets()
with unreal.ScopedSlowTask(actorsCount, slowTaskDisplayText) as ST:
ST.make_dialog(True)
for x in range(actorsCount):
if ST.should_cancel():
break
unreal.EditorLevelLibrary.spawn_actor_from_object(selectedAssets[0], unreal.Vector(1.0+x*100, 1.0+x*100, 30.0), unreal.Rotator(0.0, 0.0, 10.0*x))
unreal.log("Just added an actor to the level!")
ST.enter_progress_frame(1)

P15修改选择的资产或者actor的属性

image.pngimage.png
例如如果我们要修改这些属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import unreal

selectedAssets = unreal.EditorUtilityLibrary.get_selected_assets()

for selectedAsset in selectedAssets:
selectedAsset.set_editor_property("BlueprintDisplayName", "Some BP")
selectedAsset.set_editor_property("BlueprintCategory", "Collectable")
selectedAsset.set_editor_property("BlueprintDescription", "This is a blueprint generated by Python or something")

selectedActors = unreal.EditorLevelLibrary.get_selected_level_actors()
for actor in selectedActors:
actor.set_editor_property("canRotate", True)
actor.set_editor_property("bHidden", 1)
actor.set_editor_property("SpriteScale", 7.5)

其中bHidden和SpriteScale对应的是ActorHiddenInGame和EditorBillboardScale
之所以使用的不是ue编辑器显示的界面名字是因为ue源码中设置的这些是编辑器中显示的名字,实际上源码中使用的变量名字并不是这些,并且bHidden在ue中显示的是名字是ActorHiddenInGame而且看起来是布尔变量,但是源码中实际上是int类型的名字叫bHidden。
那么如何在源码中查找真正的变量名字与类型呢?
例如这里设置actor的属性,我们就可以去Actor.h文件中搜索ue界面编辑器中显示的名字,然后就可以查找出来真正的名字与类型。
image.png


P16 python与蓝图的结合

把P12的功能做成一个带UI的工具:
image.png
其中滑块与文本之间可以通过函数进行绑定:
在UI编辑器中选择文本控件:image.png在text处进行绑定:
参考:
image.png
python在蓝图节点中使用:
步骤:使用FormatText节点,节点中使用花括号框住的变量会自动产生引脚,为引脚传递数值配合python代码使用executePythonCommand节点执行。
image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import unreal
actorsCount = int({actorsCount})
rotationStep = int({rotationStep})
positionOffset = {positionOffset}
slowTaskDisplayText = "Spawning actors in the level...."
selectedAssets = unreal.EditorUtilityLibrary.get_selected_assets()
with unreal.ScopedSlowTask(actorsCount, slowTaskDisplayText) as ST:
ST.make_dialog(True)
for i in range(actorsCount):
if ST.should_cancel():
break
unreal.EditorLevelLibrary.spawn_actor_from_object(selectedAssets[0],unreal.Vector(positionOffset * i, positionOffset * i, 30.0), unreal.Rotator(0.0, 0.0, rotationStep * i))
unreal.log("Just added an actor to the level!")
ST.enter_progress_frame(1)

P19 清理动画序列的修改

清理动画序列的这些:
image.png

1
2
3
4
5
6
7
8
import unreal

selectedAssets = unreal.EditorUtilityLibrary.get_selected_assets()

for asset in selectedAssets:
asset.modify(True)
unreal.AnimationLibrary.remove_all_animation_notify_tracks(asset)

教程中使用的方式:
image.png

P20 清理未使用的资产

使用后将无法撤回,看看就好,并且如果资产过多会直接崩溃。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import unreal

workingPath = "/Game"

allAssets = unreal.EditorAssetLibrary.list_assets(workingPath)

processingAssetPath = ""
allAssetsCount = len(allAssets)

if (allAssetsCount > 0):
with unreal.ScopedSlowTask(allAssetsCount, processingAssetPath) as ST:
ST.make_dialog(True)
for asset in allAssets:
processingAssetPath = asset
deps = unreal.EditorAssetLibrary.find_package_referencers_for_asset(asset) # 寻找资产的依赖项
if (len(deps)<=0):
unreal.log(">>>>>> Deleteing >>>>> %s" % asset)
unreal.EditorAssetLibrary.delete_asset(asset)
if ST.should_cancel():
break
ST.enter_progress_frame(1, processingAssetPath)

P21 批量创建材质实例

这里值得注意的是,使用get_path_name得到的是path.name格式的字符串,因此如果仅仅需要path则需要替换字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import unreal

totalRequiredInstances = 15

newAssetName = ""
sourceAssetPath = ""
createdAssetsPath = ""

selectedAssets = unreal.EditorUtilityLibrary.get_selected_assets()

factor = unreal.MaterialInstanceConstantFactoryNew()

assetTools = unreal.AssetToolsHelpers.get_asset_tools()

for selectedAsset in selectedAssets:
newAssetName = selectedAsset.get_name() + "_%s_%d"
sourceAssetPath = selectedAsset.get_path_name() # /Game/MyTools/NewMaterial.NewMaterial
createdAssetsPath = sourceAssetPath.replace(selectedAsset.get_name(), "-") # /Game/MyTools/-.-
createdAssetsPath = createdAssetsPath.replace("-.-", "") # /Game/MyTools/

for x in range(totalRequiredInstances):
newAsset = assetTools.create_asset(newAssetName % ("inst", x + 1), createdAssetsPath, None, factor)
unreal.MaterialEditingLibrary.set_material_instance_parent(newAsset, selectedAsset)