with one click
animation-blueprint
// Navigate, inspect AND author Animation Blueprints, state machines, states, transitions and transition rules
// Navigate, inspect AND author Animation Blueprints, state machines, states, transitions and transition rules
Configure Niagara emitter internals - modules, renderers, rapid iteration parameters, graph positioning, and scratch-pad authoring (Custom HLSL + node graph)
Create and manage Niagara particle systems - system lifecycle, adding/copying emitters, user parameters, system script settings, scratch-pad authoring
Search, find, open, move, duplicate, save, delete, import, and export assets
Create and modify Blueprint assets, variables, functions, and components
Preview, validate, bake, and manipulate animation sequences with constraint-aware bone editing
Add, connect, and configure nodes in Blueprint event graphs and function graphs
| name | animation-blueprint |
| display_name | Animation Blueprints |
| description | Navigate, inspect AND author Animation Blueprints, state machines, states, transitions and transition rules |
| vibeue_classes | ["AnimGraphService","AssetDiscoveryService","BlueprintService"] |
| unreal_classes | ["EditorAssetLibrary"] |
| keywords | ["animation","animblueprint","abp","state machine","state","transition","transition rule","animgraph","locomotion","combat","authoring"] |
add_transition() only draws the arrow between two states. The transition's rule graph
starts empty, which evaluates to false, so the transition is inert — the state
machine will never move. You MUST set a rule on every transition:
import unreal
abp = "/Game/ABP_Character"
# 1) Add the bool/float variable the rule reads (on the AnimBP), then COMPILE so it resolves
unreal.BlueprintService.add_variable(abp, "bIsDead", "bool")
unreal.BlueprintService.compile_blueprint(abp)
# 2) Draw the transition
unreal.AnimGraphService.add_transition(abp, "Locomotion", "Idle", "Dead", 0.2)
# 3) Give it a rule — THIS is what makes it fire
unreal.AnimGraphService.set_transition_rule_from_bool(abp, "Locomotion", "Idle", "Dead", "bIsDead")
Rule options:
set_transition_rule_from_bool(abp, machine, src, dst, bool_var, invert=False)set_transition_rule_comparison(abp, machine, src, dst, float_var, op, value) — op ∈ greater/less/greater_equal/less_equal/equal/not_equalset_transition_rule_automatic(abp, machine, src, dst, trigger_time=-1.0) — fires when the source state's animation is (almost) finished; perfect for one-shots (attack→idle)clear_transition_rule(...) — non-destructively reset a ruleAfter authoring, always run validate_state_machine() (see below) and compile.
Use AnimGraphService to navigate directly to states and graphs:
import unreal
# Open an AnimBP's main AnimGraph
unreal.AnimGraphService.open_anim_graph("/Game/ABP_Character", "AnimGraph")
# Open a specific state inside a state machine
unreal.AnimGraphService.open_anim_state("/Game/ABP_Character", "Locomotion", "IdleLoop")
# Open a transition rule
unreal.AnimGraphService.open_transition("/Game/ABP_Character", "Locomotion", "Idle", "Walk")
State machine names come from the node title, not the graph name. Use list_state_machines() to get the correct names:
machines = unreal.AnimGraphService.list_state_machines("/Game/ABP_Character")
for m in machines:
print(f"Machine: {m.machine_name}") # Use THIS name for open_anim_state
All name lookups are case-insensitive, but prefer using exact names from introspection:
# Both work:
unreal.AnimGraphService.open_anim_state(path, "State Controller", "In Air Loop")
unreal.AnimGraphService.open_anim_state(path, "state controller", "in air loop")
CRITICAL: AnimBlueprints require a skeleton reference. Always find the skeleton first.
import unreal
# Step 1: Find skeleton from reference blueprint
ref_bp_path = "/Game/Blueprints/SandboxCharacter_CMC_ABP"
if unreal.EditorAssetLibrary.does_asset_exist(ref_bp_path):
ref_bp = unreal.load_asset(ref_bp_path)
skeleton = ref_bp.get_editor_property('target_skeleton')
skeleton_path = skeleton.get_path_name()
print(f"Using skeleton: {skeleton_path}")
else:
# Or load directly if you know the path
skeleton_path = "/Game/Characters/UEFN_Mannequin/Meshes/SK_UEFN_Mannequin"
skeleton = unreal.load_asset(skeleton_path)
# Step 2: Delete existing if needed
asset_path = "/Game/Tests/ABP_TestCharacter"
if unreal.EditorAssetLibrary.does_asset_exist(asset_path):
unreal.EditorAssetLibrary.delete_asset(asset_path)
# Step 3: Create AnimBlueprint with skeleton
asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
blueprint_factory = unreal.AnimBlueprintFactory()
blueprint_factory.set_editor_property("target_skeleton", skeleton)
blueprint_factory.set_editor_property("parent_class", unreal.AnimInstance)
animation_blueprint = asset_tools.create_asset(
"ABP_TestCharacter",
"/Game/Tests",
unreal.AnimBlueprint,
blueprint_factory
)
# Step 4: Save
if animation_blueprint:
unreal.EditorAssetLibrary.save_asset(asset_path, only_if_is_dirty=False)
print(f"Created: {asset_path}")
Opening in Editor:
# Use open_editor_for_assets (plural) with list
editor_subsystem = unreal.get_editor_subsystem(unreal.AssetEditorSubsystem)
opened = editor_subsystem.open_editor_for_assets([animation_blueprint])
⚠️ DO NOT use AssetEditorSubsystem.open_editor_for_asset() (singular) - it will cause AttributeError.
import unreal
abp_path = "/Game/Blueprints/SandboxCharacter_CMC_ABP"
# Get overview
parent = unreal.AnimGraphService.get_parent_class(abp_path)
skeleton = unreal.AnimGraphService.get_skeleton(abp_path)
print(f"Parent: {parent}, Skeleton: {skeleton}")
# List all graphs
graphs = unreal.AnimGraphService.list_graphs(abp_path)
for g in graphs:
print(f"Graph: {g.graph_name} ({g.graph_type}), Nodes: {g.node_count}")
import unreal
abp_path = "/Game/ABP_Character"
# Find all state machines
machines = unreal.AnimGraphService.list_state_machines(abp_path)
for machine in machines:
print(f"\nState Machine: {machine.machine_name} ({machine.state_count} states)")
# List states in this machine
states = unreal.AnimGraphService.list_states_in_machine(abp_path, machine.machine_name)
for state in states:
end_marker = " [END]" if state.is_end_state else ""
print(f" - {state.state_name} ({state.state_type}){end_marker}")
import unreal
abp_path = "/Game/Blueprints/SandboxCharacter_CMC_ABP"
# Open the AnimBP and navigate to a specific state
unreal.AnimGraphService.open_anim_state(
abp_path,
"State Controller", # State machine name
"In Air Loop" # State name
)
import unreal
abp_path = "/Game/ABP_Character"
machine_name = "Locomotion"
# Get all transitions in a state machine
transitions = unreal.AnimGraphService.get_state_transitions(abp_path, machine_name)
for t in transitions:
auto = " [AUTO]" if t.is_automatic else ""
print(f"{t.source_state} -> {t.dest_state} (blend: {t.blend_duration}s){auto}")
# Get transitions for a specific state
idle_transitions = unreal.AnimGraphService.get_state_transitions(abp_path, machine_name, "Idle")
build_state_machineBuild (or extend) a whole machine from one JSON spec in a single atomic, idempotent call. Re-running it never duplicates existing states/transitions. It sets animations, rules, the entry state, then compiles — and returns a JSON report.
import unreal, json
abp = "/Game/ABP_Character"
# Add the variables the rules read FIRST, then compile so the rules can resolve them
unreal.BlueprintService.add_variable(abp, "Speed", "float")
unreal.BlueprintService.add_variable(abp, "bAttack", "bool")
unreal.BlueprintService.compile_blueprint(abp)
spec = {
"states": [
{"name": "Idle", "animation": "/Game/Anims/Idle", "loop": True, "pos": [0, 0]},
{"name": "Walk", "animation": "/Game/Anims/Walk", "loop": True, "pos": [350, 0]},
{"name": "Attack", "animation": "/Game/Anims/Attack", "loop": False, "pos": [350, 250]},
],
"transitions": [
{"from": "Idle", "to": "Walk", "rule": {"type": "comparison", "variable": "Speed", "op": "greater", "value": 10}, "blend": 0.15},
{"from": "Walk", "to": "Idle", "rule": {"type": "comparison", "variable": "Speed", "op": "less_equal", "value": 10}},
{"from": "Idle", "to": "Attack", "rule": {"type": "bool", "variable": "bAttack"}},
{"from": "Attack", "to": "Idle", "rule": {"type": "automatic"}}, # when the attack anim ends
],
"entry": "Idle",
}
report = json.loads(unreal.AnimGraphService.build_state_machine(abp, "Locomotion", json.dumps(spec)))
print(report) # {"success":true,"states_created":3,"transitions_created":4,"errors":[]}
rule.type ∈ bool (variable, optional invert), comparison (variable,op,value),
automatic (optional trigger_time), always (always-true), or omit rule to leave it inert.
import unreal
abp = "/Game/ABP_Character"
# State machine + states
unreal.AnimGraphService.add_state_machine(abp, "Locomotion", 0, 0)
unreal.AnimGraphService.add_state(abp, "Locomotion", "Idle", 0, 0)
unreal.AnimGraphService.add_state(abp, "Locomotion", "Walk", 350, 0)
# One call: create+assign the sequence player inside the state AND wire it to Output Pose
unreal.AnimGraphService.set_state_animation(abp, "Locomotion", "Idle", "/Game/Anims/Idle", True)
unreal.AnimGraphService.set_state_animation(abp, "Locomotion", "Walk", "/Game/Anims/Walk", True)
# Default/entry state
unreal.AnimGraphService.set_entry_state(abp, "Locomotion", "Idle")
# Transitions + rules (rules are mandatory — see top gotcha)
unreal.AnimGraphService.add_transition(abp, "Locomotion", "Idle", "Walk", 0.15)
unreal.AnimGraphService.set_transition_rule_comparison(abp, "Locomotion", "Idle", "Walk", "Speed", "greater", 10.0)
unreal.AnimGraphService.set_transition_priority(abp, "Locomotion", "Idle", "Walk", 1)
unreal.BlueprintService.compile_blueprint(abp)
validate_state_machineresult = unreal.AnimGraphService.validate_state_machine(abp, "Locomotion")
print(f"valid={result.is_valid} states={result.state_count} transitions={result.transition_count}")
for e in result.errors: print("ERROR:", e) # inert transitions, no entry state
for w in result.warnings: print("WARN :", w) # unreachable states, states with no animation
Treat any errors as a build failure. The most common error is an inert transition
(a transition with no rule) — fix it with one of the set_transition_rule_* calls.
import unreal
abp_path = "/Game/ABP_Character"
sequences = unreal.AnimGraphService.get_used_anim_sequences(abp_path)
for seq in sequences:
print(f"{seq.sequence_name}")
print(f" Path: {seq.sequence_path}")
print(f" Used in: {seq.used_in_graph}")
import unreal
abp_path = "/Game/ABP_Character"
# If you have a node GUID from get_nodes_in_graph
node_id = "ABC123-DEF456-..."
unreal.AnimGraphService.focus_node(abp_path, node_id)
Python Naming Convention: C++ types like
FAnimStateMachineInfoare exposed asAnimStateMachineInfoin Python (noFprefix).
| Property | Type | Description |
|---|---|---|
machine_name | string | Display name of the state machine |
node_id | string | Node GUID |
state_count | int | Number of states |
parent_graph_name | string | Graph containing this machine |
| Property | Type | Description |
|---|---|---|
state_name | string | Display name of the state |
node_id | string | Node GUID |
state_type | string | "State", "Conduit", "Entry" |
is_end_state | bool | True if no outgoing transitions |
pos_x | float | X position in graph |
pos_y | float | Y position in graph |
| Property | Type | Description |
|---|---|---|
transition_name | string | Display name |
node_id | string | Node GUID |
source_state | string | Source state name |
dest_state | string | Destination state name |
priority | int | Priority (lower = higher) |
blend_duration | float | Crossfade time in seconds |
is_automatic | bool | Auto-transition based on sequence |
rule_type | string | None (inert), Bool, Comparison, Automatic, Custom |
rule_variable | string | Bound variable name (for Bool/Comparison rules) |
rule_summary | string | Human-readable rule, e.g. Speed > 150, bIsDead == true |
has_rule | bool | False = inert (never fires). True = transition can fire |
Returned by validate_state_machine().
| Property | Type | Description |
|---|---|---|
is_valid | bool | True when there are no blocking errors |
state_count | int | Number of states |
transition_count | int | Number of transitions |
errors | [string] | Blocking problems (inert transitions, no entry state) |
warnings | [string] | Non-blocking issues (unreachable states, states with no animation) |
import unreal
if unreal.AnimGraphService.is_anim_blueprint("/Game/SomeAsset"):
# It's an AnimBP, safe to use AnimGraphService
machines = unreal.AnimGraphService.list_state_machines("/Game/SomeAsset")
import unreal
abp_path = "/Game/ABP_Character"
machines = unreal.AnimGraphService.list_state_machines(abp_path)
for machine in machines:
states = unreal.AnimGraphService.list_states_in_machine(abp_path, machine.machine_name)
for state in states:
print(f"{machine.machine_name}/{state.state_name}")