Control Rig动画Python脚本编写

使用Python脚本驱动和扩展Control Rig动画制作。

Choose your operating system:

Windows

macOS

Linux

Python脚本可以用于自动化并控制在 SequencerControl Rig 动画制作的各个部分。该文档介绍通过Control Rig、动画模式以及其它流程制作动画时使用Python的主要方式。

先决条件

创建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]
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)