Python脚本可以用于自动化并控制在 Sequencer 中 Control Rig 动画制作的各个部分。该文档介绍通过Control Rig、动画模式以及其它流程制作动画时使用Python的主要方式。
先决条件
- 你对于虚幻引擎Python脚本编写和Sequencer脚本编写有一定经验。
- 你了解如何创建和使用Control Rig。
创建Control Rig轨道
创建Control Rig轨道与通常在Sequencer中创建轨道略有不同。Control Rig轨道需要创建一个定义的Control Rig类。要创建Control Rig轨道,使用以下指令:
# 获取编辑器世界
world = unreal.EditorLevelLibrary.get_editor_world()
# 获取Control Rig资产
rig = unreal.load_asset("/Game/Animation/ControlRig/Mannequin_ControlRig")
# 获取Rig类
rig_class = rig.get_control_rig_class()
# 通过关卡序列和Actor绑定,我们可以从类中找到或者创建Control Rig轨道
rig_track = unreal.ControlRigSequencerLibrary.find_or_create_control_rig_track(world,level_sequence, rig_class, actor_binding)
动画控制
以下示例解释了编辑动画控制的几种方式。
控制选择
以下指令可以用于选择控制并且检索控制选择:
# 获取Sequncer中的Control Rig,返回一个包含ControlRigSequencerBindingProxy的列表
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)
# 获取第一个代理,假设是Mannequin_ControlRig
rig_proxy = rig_proxies[0]
# 从ControlRigSequencerBindingProxy,我们可以获取ControlRig 对象
rig = rig_proxy.control_rig
# 选择一个指定的控制
rig.select_control("body_ctrl")
# 获取当前控制选择
selected_controls = rig.current_control_selection()
print(selected_controls)
# 清空控制选择
rig.clear_control_selection()
获取并设置控制数值
可以使用以下指令在任意帧数从任何控制中获取指定的数值:
# 获取Sequncer中的Control Rig - 返回一个包含ControlRigSequencerBindingProxy的列表
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)
# 获取第0帧
frame_num = unreal.FrameNumber(0)
# 抓取第一个代理 - 假设是Mannequin_ControlRig
rig_proxy = rig_proxies[0]
# 从ControlRigSequencerBindingProxy,我们可以获取ControlRig对象
rig = rig_proxy.control_rig
# 获取本地控制数值,每个控制类型都会有其自己的功能
transform = unreal.ControlRigSequencerLibrary.get_local_control_rig_transform(level_sequence, rig, "body_ctrl", frame_num)
bool = unreal.ControlRigSequencerLibrary.get_local_control_rig_bool(level_sequence, rig, "twist_ctrl_vis", frame_num)
print(transform)
print(bool)
你还可以通过以下指令在任意帧数给任意控制设置指定的数值:
# 获取Sequncer中的Control Rig - 返回一个包含ControlRigSequencerBindingProxy的列表
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)
# 抓取第一个代理 - 假设是Mannequin_ControlRig
rig_proxy = rig_proxies[0]
# 从ControlRigSequencerBindingProxy我们可以获取ControlRig对象
rig = rig_proxy.control_rig
# 获取当前时间
current_time = unreal.LevelSequenceEditorBlueprintLibrary.get_current_time()
# 将当前时间转换为帧数(FrameNumber)对象
current_frame = unreal.FrameNumber(current_time)
# 为控制创建适当的数值对象
transform_value = unreal.Transform(location=[0, 10, 20], rotation=[0,30,0], scale=[1,1,1])
bool_value = True
# 设置本地控制数值,每个控制类型都会有其自己的功能
# 每个类型的功能还会有一个set_key标记,默认为True
unreal.ControlRigSequencerLibrary.set_local_control_rig_transform(level_sequence, rig, "body_ctrl", frame_num, transform_value)
unreal.ControlRigSequencerLibrary.set_local_control_rig_bool(level_sequence, rig, "twist_ctrl_vis", frame_num, bool_value, set_key = False)
动画模式
动画模式工具也可以受Python脚本影响。以下是一些示例。
Tween工具
以下指令可以用于Tween 工具:
# 将tween数值设为-1 - 1之间
# -1会渲染到上一帧
# 1会渲染到下一帧
tween_value = -1
unreal.ControlRigSequencerLibrary.tween_control_rig(level_sequence, rig, tween_value)
吸附工具
以下指令可以用于吸附工具。如果驱动对象动画化,那么驱动对象必须添加至活跃序列。
# 获取Sequncer中的Control Rig - 返回一个包含ControlRigSequencerBindingProxy的列表
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)
# 抓取第一个代理 - 假设是Mannequin_ControlRig
rig_proxy = rig_proxies[0]
# 从ControlRigSequencerBindingProxy我们可以获取ControlRig对象
rig = rig_proxy.control_rig
# 获取编辑器Actor子系统来添加Actor
editor_actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
# 向编辑器世界中添加一个立方体并设置位置
cube_mesh = unreal.load_asset("/Engine/BasicShapes/Cube")
cube_location = unreal.Vector(0, 10, 20)
cube_actor = editor_actor_subsystem.spawn_actor_from_object(cube_mesh, cube_location)
# 设置起始和结束帧范围
start_frame = unreal.FrameNumber(0)
end_frame = unreal.FrameNumber(5)
# 为父级和子级创建ControlRigSnapperSelection对象
parent = unreal.ControlRigSnapperSelection()
children = unreal.ControlRigSnapperSelection()
# 创建ActorForWorldTransforms对象
# 将立方体Actor设为父级
parent_actor = unreal.ActorForWorldTransforms()
parent_actor.actor = cube_actor
# 创建ControlRigForWorldTransforms对象
# 设置到合适的Control Rig,将左手控制设为控制
# 这里可以有多个控制名称
child_control_rig = unreal.ControlRigForWorldTransforms()
child_control_rig.control_rig = rig
child_control_rig.control_names = ["hand_l_ctrl"]
# 使用ActorForWorldTransforms对象,将其设为父级ControlRigSnapperSelection
# 使用ControlRigForWorldTransforms对象,将其设为子级ControlRigSnapperSelection
parent.actors = [parent_actor]
children.control_rigs = [child_control_rig]
# 创建并设置吸附设置
snap_settings = unreal.ControlRigSnapSettings()
snap_settings.keep_offset = False
snap_settings.snap_position = True
snap_settings.snap_rotation = True
snap_settings.snap_scale = False
# 从第0-5帧将左手控制吸附到立方体上
unreal.ControlRigSequencerLibrary.snap_control_rig(level_sequence, start_frame, end_frame, children, parent, snap_settings)
空间切换
以下指令和示例可以用于空间切换.
要开始空间切换,需要创建空间关键帧。可以将控制的空间设置为其默认父级、世界空间或者任意指定帧数的另一个控制。
# 获取Sequncer中的Control Rig - 返回一个包含ControlRigSequencerBindingProxy的列表
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)
# 抓取第一个代理 - 假设是Mannequin_ControlRig
rig_proxy = rig_proxies[0]
# 从ControlRigSequencerBindingProxy,我们可以获取ControlRig对象
rig = rig_proxy.control_rig
# 在第0帧将左手控制的空间设置为世界空间
control_name = "hand_l_ctrl"
space = unreal.ControlRigSequencerLibrary.get_world_space_reference_key()
time = unreal.FrameNumber(value = 0)
unreal.ControlRigSequencerLibrary.set_control_rig_space(level_sequence, rig, control_name, space, time)
# 然后,在第30帧将空间切换设置到头部
space = unreal.RigElementKey(type = unreal.RigElementType.CONTROL, name = "head_ctrl")
time = unreal.FrameNumber(value = 30)
unreal.ControlRigSequencerLibrary.set_control_rig_space(level_sequence, rig, control_name, space, time)
# 最后,在第60帧将空间切换设置到其默认父级
space = unreal.ControlRigSequencerLibrary.get_default_parent_key()
time = unreal.FrameNumber(value = 60)
unreal.ControlRigSequencerLibrary.set_control_rig_space(level_sequence, rig, control_name, space, time)
空间关键帧创建好后,可以通过以下指令将它们移动到任意帧数:
# 获取Sequncer中的Control Rig - 返回一个包含ControlRigSequencerBindingProxy的列表
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)
# 抓取第一个代理 - 假设是Mannequin_ControlRig
rig_proxy = rig_proxies[0]
# 从ControlRigSequencerBindingProxy,我们可以获取ControlRig对象
rig = rig_proxy.control_rig
# 从设置空间关键帧示例中假设我们的空间关键帧位于第0、30和60帧的
# 左手控制上。我们将空间关键帧移动到第30和45帧
control_name = "hand_l_ctrl"
old_time = unreal.FrameNumber(value = 30)
new_time = unreal.FrameNumber(value = 45)
unreal.ControlRigSequencerLibrary.move_control_rig_space(level_sequence, rig, control_name, old_time, new_time)
可以使用以下指令来删除空间关键帧:
# 获取Sequncer中的Control Rig - 返回一个包含ControlRigSequencerBindingProxy的列表
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)
# 抓取第一个代理 - 假设是Mannequin_ControlRig
rig_proxy = rig_proxies[0]
# 从ControlRigSequencerBindingProxy,我们可以获取ControlRig对象
rig = rig_proxy.control_rig
# 从移动空间关键帧示例中假设我们的空间关键帧位于第0、45和60帧的
# 左手控制上。现在我们移除第45帧的空间关键帧
control_name = "hand_l_ctrl"
time = unreal.FrameNumber(value = 45)
unreal.ControlRigSequencerLibrary.delete_control_rig_space(level_sequence, rig, control_name, time)
可以使用以下指令将最终动画烘焙到指定的空间:
# 获取Sequncer中的Control Rig - 返回一个包含ControlRigSequencerBindingProxy的列表
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)
# 抓取第一个代理 - 假设是Mannequin_ControlRig
rig_proxy = rig_proxies[0]
# 从ControlRigSequencerBindingProxy,我们可以获取ControlRig对象
rig = rig_proxy.control_rig
# 获取所有选中的rig控制
control_names = rig.current_control_selection()
# 从关卡序列中获取起始和结束帧,为烘焙设置创建帧数(FrameNumber)对象
start_frame_num = level_sequence.get_playback_start()
end_frame_num = level_sequence.get_playback_end()
start_frame = unreal.FrameNumber(value = start_frame_num)
end_frame = unreal.FrameNumber(value = end_frame_num)
# 为空间设置烘焙设置,我们将要烘焙到默认父级空间
space_bake_settings = unreal.RigSpacePickerBakeSettings()
space_bake_settings.target_space = unreal.ControlRigSequencerLibrary.get_default_parent_key()
space_bake_settings.start_frame = start_frame
space_bake_settings.end_frame = end_frame
space_bake_settings.reduce_keys = False
space_bake_settings.tolerance = 0
unreal.ControlRigSequencerLibrary.bake_control_rig_space(level_sequence, rig, control_names, space_bake_settings)
动画模式设置
动画模式设置可以通过Python脚本来编辑。每个属性使用以下术语:
| 名称 | 描述 |
|---|---|
| bDisplayHierarchy | 在角色上绘制骨骼。 |
| bDisplayNulls | 在角色上绘制Null。 |
| bHideManipulators | 在视口中隐藏所有控制。如果启用了 Display Hierarchy 或者 Display Nulls,那么也会隐藏骨骼和Null。 |
| bCoordSystemPerWidgetMode | 在视口中改变小工具模式时恢复坐标空间。 |
| bOnlySelectRigControls | 启用后,视口中将仅能选择Control Rig控制。所有其余物体,包括角色,都不能选择。 |
| bLocalTransformsInEachLocalSpace | 启用后,如果你的转换小工具设为本地坐标,将会把每个选中的控制相对于它们的本地转换空间进行转换。 |
| GizmoScale | 放大或者缩小小工具尺寸。 |
可以使用以下指令:
# 载入模式设置类并获取默认对象
ControlRigSettingsClass = unreal.load_class(None, '/Script/ControlRigEditor.ControlRigEditModeSettings')
ControlRigSettings = unreal.get_default_object(ControlRigSettingsClass)
# 打印输出检索的数据
print(ControlRigSettings.get_editor_property('bDisplayHierarchy'))
print(ControlRigSettings.get_editor_property('bDisplayNulls'))
print(ControlRigSettings.get_editor_property(GizmoScale))
# 设置属性
ControlRigSettings.set_editor_property('bDisplayHierarchy', True)
ControlRigSettings.set_editor_property('bDisplayNulls', True)
ControlRigSettings.set_editor_property('GizmoScale', 5)
烘焙和合并
烘焙到Control Rig
如果Sequencer中Actor上已经有了一个动画序列,那么可以通过将当前动画烘焙到Control Rig来创建Control Rig轨道。请使用以下指令:
# 获取当前编辑器关卡
editor_system = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
world = editor_system.get_editor_world()
# 获取动画序列导出选项
anim_seq_export_options = unreal.AnimSeqExportOption()
anim_seq_export_options.export_transforms = True
anim_seq_export_options.export_curves = True
# 获取关键帧容忍数和关键帧减少状态
tolerance = 0.01
reduce_keys = False
# 烘焙到Control Rig
unreal.ControlRigSequencerLibrary.bake_to_control_rig(world, level_sequence, rig_class, anim_seq_export_options, False, tolerance, actor_binding)
烘焙到动画序列
Control Rig动画完成后,可以将动画作为动画序列导出,以便在虚幻引擎的其它地方使用。请使用以下指令:
# 获取当前关卡序列
level_sequence = unreal.LevelSequenceEditorBlueprintLibrary.get_current_level_sequence()
# 获取名称为SK Mannequin的SkeletaMeshActor绑定
# 这是将Mannequin_ControlRig拖进关卡编辑器时的默认名称
binding = level_sequence.find_binding_by_name("SK Mannequin")
# 抓取关卡编辑器世界
editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
world = editor_subsystem.get_editor_world()
# 创建动画序列导出选项
anim_seq_export_options = unreal.AnimSeqExportOption()
anim_seq_export_options.export_transforms = True
anim_seq_export_options.export_morph_targets = True
# 获取资产工具
# 创建空的动画序列 - /Game/Test_Anim
asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
anim_sequence = unreal.AssetTools.create_asset(asset_tools, asset_name = "Test_Anim", package_path = "/Game/", asset_class = unreal.AnimSequence, factory = unreal.AnimSequenceFactory())
# 烘焙至创建的动画序列
unreal.SequencerTools.export_anim_sequence(world, level_sequence, anim_sequence, anim_seq_export_options, binding, False)
# 如果我们想创建链接的动画序列,只需将最后一个参数改为True
unreal.SequencerTools.export_anim_sequence(world, level_sequence, anim_sequence, anim_seq_export_options, binding, True)
合并动画层级
如果你在Control Rig轨道中使用多个分段和层级,你使用以下指令可以将它们烘焙合并至一个层级:
# 获取Sequncer中的Control Rig - 返回一个包含ControlRigSequencerBindingProxy的列表
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)
# 抓取第一个代理 - 假设是Mannequin_ControlRig
rig_proxy = rig_proxies[0]
# 从ControlRigSequencerBindingProxy我们可以获取MovieSceneControlRigParameterTrack对象
# 通过该轨道,我们可以将轨道中的所有分段折叠为一个分段
rig_track = rig_proxy.track
unreal.ControlRigSequencerLibrary.collapse_control_rig_anim_layers
(level_sequence, rig_track, key_reduce = False, tolerance = 0.001)