Unreal Python代码段

列出所有Assets以及类型

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

workingPath = "/Game/"

@unreal.uclass()
class GetEditorAssetLibrary(unreal.EditorAssetLibrary):
pass

editorAssetLib = GetEditorAssetLibrary()
allAssets = editorAssetLib.list_assets(workingPath, True, False)
allAssetsCount = len(allAssets)

selectedAssetPath = workingPath
print('---'*20)
with unreal.ScopedSlowTask(allAssetsCount, selectedAssetPath) as slowTask:
slowTask.make_dialog(True)
for asset in allAssets:
_assetData = editorAssetLib.find_asset_data(asset)
_assetName = _assetData.get_asset().get_name()
_assetPathName = _assetData.get_asset().get_path_name()
_assetClassName = _assetData.get_asset().get_class().get_name()
print('{0} >>>> {1}'.format(_assetName, _assetClassName))

"""
type:
LevelSequence, Material, StaticMesh, SkeletalMesh, AnimSequence, Skeleton, PhysicsAsset
"""

输出log

1
2
3
4
5
import unreal

unreal.log('基本')
unreal.log_warning('警告')
unreal.log_error('报错')

列出所有选择的资产

概念:在Content Browser里的是Assets,在场景中的是Actor

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

@unreal.uclass()
class MyEditorUtility(unreal.GlobalEditorUtilityBase):
pass

selectedAssets = MyEditorUtility().get_selected_assets()

for asset in selectedAssets:
unreal.log(asset.get_full_name()) # 获取该资产全名(类的名称+完整的路径)
unreal.log(asset.get_fname()) # 获取该资产的名称
unreal.log(asset.get_name()) # 获取该资产的名称
unreal.log(asset.get_path_name()) # 获取该资产的路径
unreal.log(asset.get_class()) # 获取该资产的类名
unreal.log('--'*20)

更多关于ObjectBase的方法可以参考:

https://docs.unrealengine.com/4.27/en-US/PythonAPI/class/_ObjectBase.html

列出所有选择的Actor

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

@unreal.uclass()
class MyEditorUtility(unreal.GlobalEditorUtilityBase):
pass

selectedActors = MyEditorUtility().get_selection_set()

for actor in selectedActors:
unreal.log(actor.get_name()) # 获取actor的名称
unreal.log(actor.get_actor_location()) # 获取actor的loaction实例
unreal.log(actor.get_actor_transform()) # 获取actor的transform实例
unreal.log(actor.is_hidden_ed()) # actor是否隐藏
if actor.actor_has_tag("tagOne"): # actor是否有xx标签
actor.destroy_actor() # 删除actor
unreal.log_warning(">>>> Do Remove")

unreal.log("--"*20)

更多关于Actor的方法可以参考:

https://docs.unrealengine.com/4.27/en-US/PythonAPI/class/Actor.html#unreal.Actor

创建蓝图

Factory 工厂:如果需要创建一个蓝图,就要找到蓝图工厂,如果要创建一个动画就要创建动画工厂。

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

blueprintName = "MyEpicBPActorClass" # 定义蓝图的名称
blueprintPath = "/Game/AutoCreated" # 定义蓝图的路径

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)

创建进度条任务

当一些任务执行时间较长,程序长时间处于“卡死”的状态,可以使用进度条来告知用户程序并没有崩溃。

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

totalFrames = 500000
textDisplay = "This is the text displayed on a progress bar" # 进度显示的文字

with unreal.ScopedSlowTask(totalFrames, textDisplay) as ST:
ST.make_dialog(True) # 创建对话框
for i in range(totalFrames):
if ST.should_cancel(): # 当用户点击取消时
break
unreal.log("running running")
ST.enter_progress_frame(1)

参考:

https://docs.unrealengine.com/4.27/en-US/PythonAPI/class/ScopedSlowTask.html

创建多个蓝图(案例结合)

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

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

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

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)

将选中的Actor放到Level中

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

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

@unreal.uclass()
class MyEditorUtility(unreal.GlobalEditorUtilityBase):
pass

selectedAssets = MyEditorUtility().get_selected_assets()

with unreal.ScopedSlowTask(actorsCount, slowTaskDisplayText) as ST:
ST.make_dialog(True)
for x in range(actorsCount):
if ST.should_cancel():
break
# 针对关卡的操作,所有这里去找EditorLevelLibray里的方法
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)

在程序主菜单下添加自定义菜单

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

# Get the main menu class
menus = unreal.ToolMenus.get()
menu_name = 'LevelEditor.MainMenu'
menu = menus.find_menu(menu_name)

# Custom menu parameters
owner = menu.get_name()
section_name = 'PythonTools'
name = 'lingyunFX'
label = 'lingyunFX'
tool_tip = 'This is some python toolset.'

# Add and refresh
menu.add_sub_menu(owner, section_name, name, label, tool_tip)
menus.refresh_all_widgets()

为菜单添加按钮

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

# Get the menu class
menus = unreal.ToolMenus.get()
menu_name = "LevelEditor.MainMenu.lingyunFX"
menu = menus.find_menu(menu_name)

# Set the button type and label
entry = unreal.ToolMenuEntry(type=unreal.MultiBlockType.MENU_ENTRY)
entry.set_label('TEST BUTTON 01')

# Set button command
typ = unreal.ToolMenuStringCommandType.PYTHON
entry.set_string_command(typ, "", 'print "this is test button"')

# Add and refresh
section_name = ''
menu.add_menu_entry(section_name, entry)
menus.refresh_all_widgets()

section_name 这个参数后面会说到它,其实是一个定位的作用。

关于add_sub_menu和add_menu_entry的参数,可以查看官方文档:

https://docs.unrealengine.com/en-US/PythonAPI/class/ToolMenu.html

(吐槽一下简陋的官方文档,就给了个参数名)

添加工具架按钮

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

# Get the menu class
menus = unreal.ToolMenus.get()
menu_name = "LevelEditor.LevelEditorToolBar"
menu = menus.find_menu(menu_name)

# Set the button type and label
entry = unreal.ToolMenuEntry(type=unreal.MultiBlockType.TOOL_BAR_BUTTON)
entry.set_label("Test Button")

# Set button command
typ = unreal.ToolMenuStringCommandType.PYTHON
entry.set_string_command(typ, "", 'print "Hello World!"')

# Add and refresh
section_name = 'Settings'
menu.add_menu_entry(section_name, entry)
menus.refresh_all_widgets()

这里就可以看到section_name参数的意义,它会将按钮放置在Settings集的最后。
如果想把按钮放在其它地方,就得先知道控件的名称。
如何知道这些控件的名字,在这里开启:

开启后重启Unreal就可以看到,如下图所示:

右键菜单扩展

这里想把按钮放到Source Control类别下,所以需要去查看控件名。

通过上面的方法可以看到,名称为:PathContextSourceControl

找到控件名,则可以用代码实现效果:

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

# Get the menu class
menus = unreal.ToolMenus.get()
menu_name = "ContentBrowser.FolderContextMenu"
menu = menus.find_menu(menu_name)

# Set the button type and label
entry = unreal.ToolMenuEntry(type=unreal.MultiBlockType.MENU_ENTRY)
entry.set_label("Right Click Test")

# Set button command, add button
typ = unreal.ToolMenuStringCommandType.PYTHON
entry.set_string_command(typ, "", 'print "entry test"')
menu.add_menu_entry('PathContextSourceControl', entry)

右键菜单是点击的时候实时刷新的,所以这里不需要再调用refresh_all_widgets()