Python Scripting for Animating with Control Rig

Drive and extend your Control Rig animation workflow using Python scripting.

Choose your operating system:

Windows

macOS

Linux

Python scripting can be used to automate and control various parts of animating your Control Rig in Sequencer. This document provides an overview of the main ways to use Python with animating your Control Rig, Animation Mode, and other various animation workflows.

Prerequisites

Create Control Rig Track

Creating a Control Rig track is slightly different from normally creating Tracks in Sequencer. A Control Rig track needs to have a Control Rig class defined to be created. To create a Control Rig Track, use the following commands:

# Get the Editor world
world = unreal.EditorLevelLibrary.get_editor_world()

# Get the control rig asset
rig = unreal.load_asset("/Game/Animation/ControlRig/Mannequin_ControlRig")

# Get the rig class
rig_class = rig.get_control_rig_class()

# Using the level sequence and actor binding, we can either find or create a control rig track from the class
rig_track = unreal.ControlRigSequencerLibrary.find_or_create_control_rig_track(world,level_sequence, rig_class, actor_binding)

Animating Controls

The following examples explain a variety of ways you can edit and animate controls.

Control Selection

These commands can be used to select controls and query control selection:

  # Get the Control Rigs in Sequencer, returns a list of ControlRigSequencerBindingProxy
  rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)

  # Get the first proxy, assuming it is Mannequin_ControlRig
  rig_proxy = rig_proxies[0]

  # From the ControlRigSequencerBindingProxy, we can get the ControlRig object
  rig = rig_proxy.control_rig

  # Selects a specified control
  rig.select_control("body_ctrl")

  # Get the current control selection
  selected_controls = rig.current_control_selection()
  print(selected_controls)

  # Clear the control selection
  rig.clear_control_selection()

Get and Set Control Values

You can get specific values from any of the controls at any given frame number with the following commands:

# Get the Control Rigs in Sequencer - returns a list of ControlRigSequencerBindingProxy
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)

# Get frame 0
frame_num = unreal.FrameNumber(0)

# Grab the first proxy - let's assume it is the Mannequin_ControlRig
rig_proxy = rig_proxies[0]

# From the ControlRigSequencerBindingProxy, we can get the ControlRig object
rig = rig_proxy.control_rig

# Gets the local control values, each control type will have their own typed function
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)

You can also set specific values on any of the controls at any given frame number using these commands:

# Get the Control Rigs in Sequencer - returns a list of ControlRigSequencerBindingProxy
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)

# Grab the first proxy - let's assume it is the Mannequin_ControlRig
rig_proxy = rig_proxies[0]

# From the ControlRigSequencerBindingProxy, we can get the ControlRig object
rig = rig_proxy.control_rig

# Get the current time
current_time = unreal.LevelSequenceEditorBlueprintLibrary.get_current_time()

# Convert current time to a FrameNumber object
current_frame = unreal.FrameNumber(current_time)

# Create the proper value objects for the controls
transform_value = unreal.Transform(location=[0, 10, 20], rotation=[0,30,0], scale=[1,1,1])

bool_value = True

# Sets the local control values, each control type will have their own typed function
# Each typed function will also have a set_key flag that is by default 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)

Animation Mode

Animation Mode Tools can also be affected with Python scripting. The following examples are provided.

Tween Tool

The following commands can be used for the Tween Tool:

# Set tween value between -1 - 1
# -1 will blend to previous frame
# 1 will blend to next frame
tween_value = -1

unreal.ControlRigSequencerLibrary.tween_control_rig(level_sequence, rig, tween_value)

Snapper Tool

The following commands can be used for the Snapper Tool. If the driver object is animated, then the driver object has to be added to the active sequence.

# Get the Control Rigs in Sequencer - returns a list of ControlRigSequencerBindingProxy
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)

# Grab the first proxy - let's assume it is the Mannequin_ControlRig
rig_proxy = rig_proxies[0]

# From the ControlRigSequencerBindingProxy, we can get the ControlRig object
rig = rig_proxy.control_rig

# Get the editor actor subsystem to add an actor
editor_actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

# Add a cube to the editor world and set the location
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)

# Set start and end frame ranges
start_frame = unreal.FrameNumber(0)
end_frame = unreal.FrameNumber(5)

# Create ControlRigSnapperSelection objects for the parent and the children
parent = unreal.ControlRigSnapperSelection()
children = unreal.ControlRigSnapperSelection()

# Create an ActorForWorldTransforms object
# Set the cube actor as the parent
parent_actor = unreal.ActorForWorldTransforms()
parent_actor.actor = cube_actor

# Create a ControlRigForWorldTransforms object
# Set to the proper control rig and the left hand control as the control
# It is possible to have multiple control names in here
child_control_rig = unreal.ControlRigForWorldTransforms()
child_control_rig.control_rig = rig
child_control_rig.control_names = ["hand_l_ctrl"]

# Take the ActorForWorldTransforms object, set it as the parent ControlRigSnapperSelection
# Take the ControlRigForWorldTransforms object, set as the child ControlRigSnapperSelection
parent.actors = [parent_actor]
children.control_rigs = [child_control_rig]

# Create and set snap settings
snap_settings = unreal.ControlRigSnapSettings()
snap_settings.keep_offset = False
snap_settings.snap_position = True
snap_settings.snap_rotation = True
snap_settings.snap_scale = False

# Then snap the left hand control to the cube from frame 0-5
unreal.ControlRigSequencerLibrary.snap_control_rig(level_sequence, start_frame, end_frame, children, parent, snap_settings)

Space Switching

The following commands and examples can be used for Space Switching.

To start space switching, you will need to create space keyframes. You can set a control's space to its default parent, world space, or to another control at any given frame number.

# Get the Control Rigs in Sequencer - returns a list of ControlRigSequencerBindingProxy
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)

# Grab the first proxy - let's assume it is the Mannequin_ControlRig
rig_proxy = rig_proxies[0]

# From the ControlRigSequencerBindingProxy, we can get the ControlRig object
rig = rig_proxy.control_rig

# Set the space of the left hand control to world space at frame 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)

# Afterwards, set space switch to the head control at frame 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)

# Then lastly, set space switch to it's default parent at frame 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)

Once space keyframes are created, you can move them to any frame number by using these commands:

# Get the Control Rigs in Sequencer - returns a list of ControlRigSequencerBindingProxy
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)

# Grab the first proxy - let's assume it is the Mannequin_ControlRig
rig_proxy = rig_proxies[0]

# From the ControlRigSequencerBindingProxy, we can get the ControlRig object
rig = rig_proxy.control_rig

# Let's assume from the Set Space Key example that we have space keys at 0, 30, and 60
# on the left hand control. Let's move the space key from frame 30 to frame 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)

Space keyframes can be deleted by using these commands:

# Get the Control Rigs in Sequencer - returns a list of ControlRigSequencerBindingProxy
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)

# Grab the first proxy - let's assume it is the Mannequin_ControlRig
rig_proxy = rig_proxies[0]

# From the ControlRigSequencerBindingProxy, we can get the ControlRig object
rig = rig_proxy.control_rig

# Let's assume from the Move Space Key example that we have space keys at 0, 345 and 60
# on the left hand control. Let's remove the space key from frame 45
control_name = "hand_l_ctrl"
time = unreal.FrameNumber(value = 45)

unreal.ControlRigSequencerLibrary.delete_control_rig_space(level_sequence, rig, control_name, time)

You can bake the final animation to a specific space by using the following commands:

# Get the Control Rigs in Sequencer - returns a list of ControlRigSequencerBindingProxy
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)

# Grab the first proxy - let's assume it is the Mannequin_ControlRig
rig_proxy = rig_proxies[0]

# From the ControlRigSequencerBindingProxy, we can get the ControlRig object
rig = rig_proxy.control_rig

# Get all selected rig controls
control_names = rig.current_control_selection()

# Get the start and end frame from the level sequence, create FrameNumber objects for bake settings
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)

# Set baking settings for space, we will be baking to the default parent space

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)

Animation Mode Settings

Animation Mode Settings can be edited using Python scripting. Each property uses the following terminology:

Name

Description

bDisplayHierarchy

Draws Bones on the character.

bDisplayNulls

Draws Nulls on the character.

bHideManipulators

Hides all Controls in the Viewport. This will also hide Bones and Nulls if Display Hierarchy or Display Nulls are enabled.

bCoordSystemPerWidgetMode

Restores the coordinate space when changing Gizmo modes in the Viewport.

bOnlySelectRigControls

Enabling this will only make Control Rig controls be selectable in the Viewport. All other objects, including the character, will not be selectable.

bLocalTransformsInEachLocalSpace

Enabling this will transform each selected control relative to their local transform space, if your transformation gizmo is set to local coordinates.

GizmoScale

Increases or decreases the gizmo scale.

The following commands can be used:

# load the mode settings class and get default object
ControlRigSettingsClass = unreal.load_class(None, '/Script/ControlRigEditor.ControlRigEditModeSettings')
ControlRigSettings = unreal.get_default_object(ControlRigSettingsClass)

# print out the queried data
print(ControlRigSettings.get_editor_property('bDisplayHierarchy'))
print(ControlRigSettings.get_editor_property('bDisplayNulls'))
print(ControlRigSettings.get_editor_property(GizmoScale))

# set the properties
ControlRigSettings.set_editor_property('bDisplayHierarchy', True)
ControlRigSettings.set_editor_property('bDisplayNulls', True)
ControlRigSettings.set_editor_property('GizmoScale', 5)

Baking and Merging

Bake to Control Rig

If you already have an animation sequence on your Actor in Sequencer, you can create a Control Rig Track by baking the current animation to a Control Rig. To do this, use the following commands:

# Get the current editor level
editor_system = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
world = editor_system.get_editor_world()

# Get the animation sequence export options
anim_seq_export_options = unreal.AnimSeqExportOption()
anim_seq_export_options.export_transforms = True
anim_seq_export_options.export_curves = True

# Get key tolerance number and key reduction state
tolerance = 0.01
reduce_keys = False

# Bake to Control Rig
unreal.ControlRigSequencerLibrary.bake_to_control_rig(world, level_sequence, rig_class, anim_seq_export_options, False, tolerance, actor_binding)

Bake to Animation Sequence

Once a Control Rig animation is completed, you can export the animation as an Animation Sequence to be used in other areas of Unreal Engine by using the following commands:

# Get the current level sequence
level_sequence = unreal.LevelSequenceEditorBlueprintLibrary.get_current_level_sequence()

# Get the SkeletaMeshActor binding called SK Mannequin
# This is the default name when dragging a Mannequin_ControlRig into the Level Editor
binding = level_sequence.find_binding_by_name("SK Mannequin")

# Grab the Level Editor World
editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
world = editor_subsystem.get_editor_world()

# Create animation sequence export options
anim_seq_export_options = unreal.AnimSeqExportOption()
anim_seq_export_options.export_transforms = True
anim_seq_export_options.export_morph_targets = True

# Get asset tools
# Create an empty AnimSequence - /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())

# Bake to the created AnimSequence
unreal.SequencerTools.export_anim_sequence(world, level_sequence, anim_sequence, anim_seq_export_options, binding, False)

# If we want to create a linked animation sequence, simply change the last arg to True
unreal.SequencerTools.export_anim_sequence(world, level_sequence, anim_sequence, anim_seq_export_options, binding, True)

Merging Animation Layers

If you are using multiple sections and layers within the Control Rig Track, you can bake and merge their animations into a single layer using these collapse commands:

# Get the Control Rigs in Sequencer - returns a list of ControlRigSequencerBindingProxy
rig_proxies = unreal.ControlRigSequencerLibrary.get_control_rigs(level_sequence)

# Grab the first proxy - let's assume it is the Mannequin_ControlRig
rig_proxy = rig_proxies[0]

# From the ControlRigSequencerBindingProxy, we can get the MovieSceneControlRigParameterTrack object
# With the track, we can collapse all the sections in the track to one section
rig_track = rig_proxy.track

unreal.ControlRigSequencerLibrary.collapse_control_rig_anim_layers
(level_sequence, rig_track, key_reduce = False, tolerance = 0.001)