Scripting Control Rig with Python in Unreal Engine 5.8: bones, controls and graph nodes
Build Control Rig hierarchies and graphs from Python in UE 5.8 with ControlRigBlueprint, RigHierarchyController and RigVMController.
If you have written rig builders in Maya or Houdini, the idea behind Control Rig's Python API will be familiar. You create the skeleton and controls from code, add graph nodes from code, and rebuild the rig whenever the template changes. Unreal's version is editor-only and splits the work between a few objects. This post maps those objects and gives short snippets for the common jobs.
Every class and method below was checked against the Unreal Python API reference for 5.8, which Epic labels Experimental. We ran the snippets in Unreal Engine 5.8.4 on Oct 27, 2026, in order in one editor session, and they worked as written. Treat them as a starting point, and read the Python Log (covered below) to confirm exact calls in your build.
The objects you work with
Epic's Control Rig Python page describes the system as Model-View-Controller. In Python terms:
unreal.ControlRigBlueprintis the asset. It derives fromRigVMBlueprint, which is where the graph methods live.unreal.RigVMControlleredits a graph. Epic's docs say the controller is "the sole authority to perform changes on the Graph." You get one from the blueprint withget_controller(graph)orget_controller_by_name(graph_name).unreal.RigHierarchyholds bones, nulls, controls and curves. It is available asrig.hierarchyorrig.get_hierarchy(), and you read from it.unreal.RigHierarchyControllerchanges the hierarchy. Get it fromrig.get_hierarchy_controller()orrig.hierarchy.get_controller().unreal.RigElementKeyidentifies an element bytype(aRigElementTypesuch asBONE,NULLorCONTROL) andname. Most hierarchy calls take and return these keys.
The editor-side code is in the ControlRigDeveloper module, and Epic's examples load it first with unreal.load_module('ControlRigDeveloper').
Getting a rig
There are three ways in: load an existing asset, pick up rigs already open in the editor, or create one. ControlRigBlueprintFactory has two class methods for creating a rig, and both take an optional modular_rig flag.
import unreal
unreal.load_module('ControlRigDeveloper')
# Rigs currently open in Control Rig editors
open_rigs = unreal.ControlRigBlueprint.get_currently_open_rig_blueprints()
# A new, empty rig asset
rig = unreal.ControlRigBlueprintFactory.create_new_control_rig_asset(
desired_package_path='/Game/Rigs/CR_Prop')
# Or a rig built from a skeletal mesh or skeleton:
# mesh = unreal.load_object(name='/Game/Chars/SK_Hero.SK_Hero', outer=None)
# rig = unreal.ControlRigBlueprintFactory.create_control_rig_from_skeletal_mesh_or_skeleton(
# selected_object=mesh)If /Game/Rigs/CR_Prop already exists, create_new_control_rig_asset doesn't fail: in our run it made /Game/Rigs/CR_Prop1 instead. To attach a preview mesh, use rig.set_preview_mesh(preview_mesh=mesh).
Building the hierarchy
RigHierarchyController.add_bone takes a name, a parent key, a transform and transform_in_global (default True), and it returns the new key. To add a bone with no parent, pass an empty RigElementKey(), as Epic's own example does. add_null has the same shape. For an existing skeleton, import_bones, import_bones_from_asset and import_bones_from_skeletal_mesh bring in bones in bulk.
hc = rig.get_hierarchy_controller()
root = hc.add_bone(name='root', parent=unreal.RigElementKey(),
transform=unreal.Transform())
lid = hc.add_bone(name='lid', parent=root,
transform=unreal.Transform(location=[0, 0, 10]))A control also needs a RigControlSettings and an initial value. The settings carry animation_type (for example RigControlAnimationType.ANIMATION_CONTROL, which the docs describe as "a visible, animatable control") and control_type, such as RigControlType.EULER_TRANSFORM, FLOAT or BOOL. You build the value with one of the RigHierarchy.make_control_value_from_* class methods.
settings = unreal.RigControlSettings()
settings.set_editor_property('animation_type',
unreal.RigControlAnimationType.ANIMATION_CONTROL)
settings.set_editor_property('control_type', unreal.RigControlType.EULER_TRANSFORM)
settings.set_editor_property('shape_visible', True)
value = unreal.RigHierarchy.make_control_value_from_euler_transform(
unreal.EulerTransform())
lid_ctrl = hc.add_control(name='lid_ctrl', parent=unreal.RigElementKey(),
settings=settings, value=value)Reading is done on the hierarchy. get_bones(), get_controls() and get_all_keys() return key arrays, get_global_transform(key, initial=False) returns a transform, and get_selected_keys() returns what the rigger has selected. The controller covers the rest of the editing: set_parent, add_parent (for multi-parent elements), rename_element, remove_element and set_control_settings.
Watch the undo defaults. add_bone and add_null default to setup_undo=False, while add_control and add_curve default to True. If you want a build to undo cleanly, set the flag explicitly.
Adding nodes to the graph
Graph nodes are added through a RigVMController. add_unit_node takes a script struct, a method name (default 'Execute'), a position and an optional node name. You get the struct with static_struct(), which every Python struct wrapper inherits.
graph = rig.get_default_model()
ctrl = rig.get_controller(graph)
ctrl.open_undo_bracket('Add float add')
node = ctrl.add_unit_node(
script_struct=unreal.RigVMFunction_MathFloatAdd.static_struct(),
method_name='Execute',
position=unreal.Vector2D(0, 0),
node_name='AddOffset')
ctrl.close_undo_bracket()
rig.recompile_vm()The Control Rig scripting page uses unreal.RigUnit_MathFloatAdd for this example. The 5.8 API reference marks that name as deprecated and notes that it "was renamed to 'RigVMFunction_MathFloatAdd'." Expect the same rename for other math nodes in older snippets.
To wire nodes, use add_link(output_pin_path, input_pin_path) and break_link with the same arguments. Pin paths are strings. The easiest way to get the exact form is to make a link by hand and read it from the Python Log. Related calls include add_template_node, add_variable_node, add_comment_node and remove_node. On the blueprint, add_member_variable(name, cpp_type, ...) creates rig variables, and recompile_vm_if_required() and request_auto_vm_recompilation() are alternatives to a forced recompile.
Let the editor write the first draft
Two features save more time than any snippet.
The Control Rig Python Log. Epic's docs describe it as "a text log of all the actions that you performed within the Control Rig Editor," including the viewport, the hierarchy and the graph. To open it, choose Window > Message Log in the Control Rig editor and select Control Rig Python Log. You can copy lines with Ctrl+C and paste them into the Output Log in Python mode.
Python Context. Commands need to know which rig they target. Clicking Run Python Context in the Class Settings Details panel sets the current rig as that context.
For an existing rig there is also generate_python_commands(), which returns an array of strings. It exists on RigVMBlueprint, RigVMController and RigHierarchyController. The 5.8 reference does not say exactly what the output covers, so test it on a copy of a real rig before you build a pipeline on it.
Caveats for 5.8
- The API is marked Experimental. Pin your scripts to an engine version and re-check signatures when you upgrade.
- Some examples on the scripting page are out of date. Besides the renamed math unit, the page calls
ControlRigBlueprint.get_available_rig_units()andrequest_control_rig_init(), and neither appears on the 5.8 class pages. The 5.8 reference hasRigVMController.get_registered_unit_structs()andRigVMBlueprint.request_rig_vm_init(). Trust the class reference over the prose page. - Modular rigs have their own controller.
get_modular_rig_controller()returns aModularRigController, and the asset can switch between the standalone and module forms withturn_into_control_rig_module()andturn_into_standalone_rig(). - Physics components go through the component API. Control Rig Physics stores its bodies and joints as hierarchy components, and
RigHierarchyController.add_componentexists. This post has not verified a physics setup from Python. For now, build physics in the graph with the RigPhysics nodes.
Further reading
- Control Rig Python Scripting in Unreal Engine (Epic, 5.8 docs, accessed 2026-09-24)
- Python Scripting for Animating with Control Rig, covering the Sequencer side
- unreal.ControlRigBlueprint (5.8)
- unreal.RigVMBlueprint (5.8)
- unreal.RigVMController (5.8)
- unreal.RigHierarchyController (5.8)
- unreal.RigHierarchy (5.8)
- unreal.RigControlSettings (5.8)
- unreal.ControlRigBlueprintFactory (5.8)



