UE开发的一点心得经验(一)

前言

最近在做一些unreal的开发,语言上使用的是python(对于我自己能力而言,使用python效率会快一些,C++只会个基础)。

但众所周知,unreal的python文档比较难阅读,很多命令和方法都得靠猜和尝试。

刚开始做unreal开发的时,要实现一些功能都得从搜索引擎去获得一些代码参考。
无法直接从帮助文档中找到想要的结果。不过到后来熟悉了unreal的“套路”,也总结了一些开发经验和心得,在这里分享给大家。

unreal版本是4.26,有些模块需要开启Sequencer Scripting和Editor Scripting Utilities插件。

maya到unreal概念转换

熟悉unreal的朋友可以跳过,这里为了给maya初入unreal的开发者传达几个概念。

首先是Level(关卡),这里可以把一个Level看作是一个maya文件。
打开一个Level,在右侧的World Outliner就是这个“maya”文件的大纲视图。
里面的所有物体,都被称作为Actor

Content Browser相当于整个项目的资产库,里面的物体类型被称作为Asset

关于Actor

获取大纲视图选择的Actor

cmds.ls(sl=True) 应该是maya开发者很常用的命令。
我们在开发一个工具时,要对物体进行操作,首先要获取物体对象。

在unreal里这样来获取:

1
2
3
4
5
import unreal

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

返回一个列表,里面是Actor类对象。

Actor对象属性修改

image.png

这里用一个摄像机来举例,修改它的FilmbackFocusMethod属性:

image.png

首先要获取到这个camera actor对象,用上面讲的方法:

1
cam = get_selected_actors()[0]

打印一下它,可以看到它是一个CineCameraActor对象,
然后去文档看看有没有setAttr之类的方法,结果一个”set”打头的都没有。

观察摄像机的属性栏(Details),可以看到有类似于maya里Transform和Shape节点的关系:
SceneComponent下有个CameraComponent,而我们想要修改的FilmbackFocusMethod属性是属于CameraComponent的。

image.png

那怎么获取到这个”Shape”呢?帮助文档里刚好有这个方法。

image.png

1
component = cam.get_cine_camera_component()

print一下可以看到它返回的是CineCameraComponent对象,在文档里找到了filmback的属性。

image.png

这个属性下面的Type是CameraFilmbackSetting,点开看看它要什么样的参数:

image.png

知道它需要什么样的参数了,我们直接把参数传进去。

1
2
3
component.filmback = unreal.CameraFilmbackSettings(sensor_width=24.889999,
sensor_height=18.67,
sensor_aspect_ratio=1.33)

生效了!unreal里的属性发生了变化。

image.png

这里把文档往下翻翻,还可以直接去设置一个预设。

1
component.set_filmback_preset_by_name('16:9 Film')

或者可以偷懒一点,直接把参数传进去:

1
component.filmback = (36.000000, 20.250000, 1.777778)

然后是FocusMethod属性设置,跟上述的步骤一样,
看它要什么参数类型然后直接传入就行了。

1
2
3
4
5
6
focus_settings = unreal.CameraFocusSettings()
focus_settings.focus_method = unreal.CameraFocusMethod.DISABLE
component.focus_settings = focus_settings

# 或者
component.focus_settings = unreal.CameraFocusSettings(focus_method=unreal.CameraFocusMethod.DISABLE)

获取所有Actor以及类型过滤

1
2
3
4
5
6
7
import unreal

# 获取当前Level
world = unreal.EditorLevelLibrary.get_editor_world()

# 获取指定Level下所有的Actor
actors = unreal.GameplayStatics.get_all_actors_of_class(world, unreal.Actor)

如果要进行类型过滤:

1
2
3
# 打印出它的class类型名称,对它进行if判断就行
for actor in actors:
print(actor.get_class().get_name())

每一个unreal对象都有get_class()方法。

关于Asset

获取选择的asset

image.png

1
2
3
4
5
6
7
import unreal

def get_selected_assets():
return unreal.EditorUtilityLibrary.get_selected_assets()

assets = get_selected_assets()
print(assets)

image.png

它返回的是一个列表,里面是unreal.Object对象,继承了unreal._ObjectBase这个基类。
有什么方法属性可以查这个文档

获取所有assets

1
2
3
4
5
import unreal

destination_path = '/Game'
assets = unreal.EditorAssetLibrary.list_assets(destination_path)
print(assets)

Content Browser拥有文件夹层级结构,它的根目录名为”/Game”,这里就是列出根目录下所有资产。
EditorAssetLibray更多方法可以参考文档,比如删除,移动,复制,读取资产等操作都在里面。

导入一个asset

1.创建一个导入任务

abc和fbx类型都可以使用AssetImportTask来导入,

image.png

首先根据参数创建一个导入的任务

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

fbx_path = r'D:\temp\Salute.fbx'
file_name = 'Salute'

task = unreal.AssetImportTask() # 实例化一个导入任务
task.set_editor_property('filename', fbx_path) # 设置导入的fbx路径
task.set_editor_property('automated', True) # 不显示对话框
task.set_editor_property('destination_path', r'/Game/test') # 导入到内容浏览器的文件夹位置
task.set_editor_property('destination_name', file_name) # 设置文件名

这里说下set_editor_property()这个方法,它是所有unreal对象都拥有的一个方法。
它可以传入参数名和参数,来设置这个对象的属性。(参考Unreal Object基类

在设置属性的时候可以调用这个方法,也可以直接传入参数,像这样:

1
2
3
4
5
6
7
8
9
import unreal

fbx_path = r'D:\temp\Salute.fbx'
file_name = 'Salute'
task = unreal.AssetImportTask()
task.filename = fbx_path
task.automated = True
task.destination_path = r'/Game/test'
task.destination_name = file_name

现在我们得到了一个导入任务的实例化对象task,还需要执行一个命令来进行实际的导入操作。

1
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])

这里传入的参数是一个列表,就是说你可以实例化多个任务一起导入。
如果中间导入任务出错了,它会继续进行下一个任务,然后在最后弹出Error信息的对话框。

2.导入设置

刚刚的导入使用的是默认选项,如果要对导入设置进行修改,调用FbxImportUI()

image.png

首先说说上述窗口的一些勾选项,按照惯例查看FbxImportUI文档

image.png

可以通过名字找到这些属性,直接进行设置就行。

1
2
3
4
5
task.options = unreal.FbxImportUI()
task.options.import_materials = False
task.options.import_animations = True
task.options.import_as_skeletal = True
task.options.import_mesh = False

接着看Import Content Type属性,它是一个下拉框。
继续翻文档,看着名称比较像的就是这个。

image.png

点开它的参数类型,FBXImportType查看

image.png

用代码来实现:

1
task.options.mesh_type_to_import = unreal.FBXImportType.FBXIT_ANIMATION

当然也可以用:

1
task.options.set_editor_property('mesh_type_to_import', unreal.FBXImportType.FBXIT_ANIMATION)

然后是skeleton选项的设置,在对话框里是选择一个Skeleton的资产。

image.png

FbxImportUI的帮助文档里有skeleton的属性,点开它的参数类型得知它需要一个Skeleton对象。
这个对象其实就是Content Browser里的一个skeleton asset。

之前说过怎么获取被选择的asset,下面是通过路径来获取asset,然后传入给属性就可以了。

1
2
skeleton_asset = unreal.load_asset('/Game/test/Salute_Skeleton')
task.options.skeleton = skeleton_asset

接下来是几个标签里的选项,AnimationTransformMaterial等。
可以在下面的这些属性去找:

  • anim_sequence_import_data
  • static_mesh_import_data
  • skeletal_mesh_import_data
  • texture_import_data

你要问我是怎么知道是这些属性的,那就是可以一一点开它们,
查看它们拥有哪些属性,对照看一下就可以猜出来。
套路还是那个套路:

根据名称在帮助文档里找到比较接近的属性名,然后点进去看它需要的参数类型。

往下就不细说了,参考代码吧,规律一看就可以明白。
针对有骨骼的情况,导入动画。完整的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fbx_path = r'D:\temp\Salute_ani.fbx'
file_name = 'Salute_ani'
task = unreal.AssetImportTask()
task.filename = fbx_path
task.destination_path = r'/Game/test'
task.destination_name = file_name
task.replace_existing = True
task.automated = True
task.save = True

skeleton_asset = unreal.load_asset('/Game/test/Salute_Skeleton')

task.options = unreal.FbxImportUI()
task.options.import_materials = False
task.options.import_animations = True
task.options.import_as_skeletal = True
task.options.import_mesh = False
task.options.skeleton = skeleton_asset
task.options.anim_sequence_import_data.import_translation = unreal.Vector(0.0, 0.0, 0.0)
task.options.anim_sequence_import_data.import_rotation = unreal.Rotator(90.0, 0.0, 0.0)
task.options.anim_sequence_import_data.import_uniform_scale = 1.0

unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])

总结

这篇文章差不多到这里,写太多了有点像流水账了。
总的来说知道了unreal文档的套路,要找到方法还是比较快的。
下篇我们继续聊聊关于sequencer的一些code与经验分享。


觉得文章还不错,可以关注这个公众号thx!