blueprints
DevelopmentCreate and modify Blueprint assets, variables, functions, components, event dispatchers, and interfaces. Use when the user asks to create a Blueprint/BP, add a variable or component to a Blueprint, make an event dispatcher/delegate, implement a Blueprint interface, or inspect a Blueprint's class/components. For node-level graph wiring, also load blueprint-graphs.
How to use this skill
Bring this guide into your coding agent with a prompt tailored to the tool you use.
- Open your project in Codex.
- Copy and paste the prompt below.
- Review files and risks before approving any action.
Inspect the Agent Skill at https://github.com/kevinpbuckley/VibeUE/blob/HEAD/Content/Skills/blueprints/SKILL.md and all supporting files. Report provenance, permissions, scripts, dependencies, and security risks. Propose an exact installation into .agents/skills/blueprints, but wait for my approval before writing files or running scripts.
Copying this prompt does not install or run the skill. Review third-party files before use.
š§ Brains complement: IF an
unreal-engine-skills-managertool (external MCP) exists in this session, call it with{action: "load", skill: "blueprint-fundamentals"}for UE domain knowledge on this topic ā correct APIs, architecture, best practices ā and treat it as the rubric for any review / "best practices" question. If no such tool is available (e.g. running under Claude Code or Codex without that MCP), skip this line entirely and proceed with this skill alone ā do NOT attempt the call.
Blueprint System Skill
For node-level graph editing (adding nodes, connecting pins, wiring logic, timers, layout), load the
blueprint-graphsskill.
Critical Rules
ā ļø Engine BlueprintTools args are {refPath} objects, NOT strings
Every UObject/UClass argument to the engine BlueprintTools toolset (blueprint, asset_type,
graph, parent_class, ā¦) is a reference object {"refPath": "<object path>"}, and these tools
return the same shape. Passing a plain string fails with
"<x> is not a valid object path for property '<arg>'". This rule is only for the engine
BlueprintTools call_tool path ā VibeUE's own unreal.BlueprintService.* Python methods still take
plain string paths.
The refPath must be the full object path ā /Game/Dir/Asset.Asset (the asset name repeated
after the dot). A bare package path (/Game/Dir/Asset, no .Asset) is rejected. Two safe ways
to get one:
# (a) Reuse what create/load returns ā it is already a {refPath} object:
bp_ref = call_tool("create", "editor_toolset.toolsets.blueprint.BlueprintTools",
{"folder_path": "/Game/BP", "asset_name": "BP_X",
"asset_type": {"refPath": "/Script/Engine.Actor"}})["returnValue"]
# (b) Build one from a package path you already have:
def bp_ref_of(p): # "/Game/Dir/BP_X" -> {"refPath": "/Game/Dir/BP_X.BP_X"}
name = p.rsplit("/", 1)[-1].split(".")[0]
return {"refPath": p if "." in p.rsplit("/", 1)[-1] else f"{p}.{name}"}
All the arguments={"blueprint": path, ...} / {"blueprint": bp, ...} examples in the sub-docs are
shorthand ā wrap the path with bp_ref_of(...) (or reuse the returned ref) when you actually call.
On success these tools return {"returnValue": null}; an error comes back as a Parameter error.
ā ļø Creating a Blueprint ā engine BlueprintTools.create
Blueprint basics now live on the engine's BlueprintTools toolset (VibeUE extends the native MCP
endpoint). Create a Blueprint with call_tool, not unreal.BlueprintService.create_blueprint (that
method was cut):
# CORRECT ā folder_path + asset_name are strings; asset_type is a {refPath} class reference.
result = call_tool(
tool_name="create",
toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",
arguments={"folder_path": "/Game/Blueprints", "asset_name": "BP_MyActor",
"asset_type": {"refPath": "/Script/Engine.Actor"}},
)
# -> {"returnValue": {"refPath": "/Game/Blueprints/BP_MyActor.BP_MyActor"}}
bp_ref = result["returnValue"] # reuse this {refPath} object for later BlueprintTools calls
The folder and asset name are passed separately (folder_path + asset_name) ā do not jam a full
asset path into one argument. asset_type is the parent class as a {refPath}:
/Script/Engine.Actor, /Script/Engine.Pawn, /Script/Engine.Character, or a Blueprint's generated
class "/Game/.../BP_Base.BP_Base_C".
Blueprint Types in add_variable ā engine BlueprintTools.add_variable
Adding a variable is also an engine BlueprintTools call now (unreal.BlueprintService.add_variable
was cut). Use call_tool with blueprint, name, type_name (and add_object_variable /
add_struct_variable for object- and struct-typed variables):
# CORRECT ā blueprint is a {refPath} object; name/type_name are strings
call_tool(
tool_name="add_variable",
toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",
arguments={"blueprint": {"refPath": "/Game/BP_MyActor.BP_MyActor"}, "name": "Health", "type_name": "float"},
)
When the variable's type is a Blueprint class, use the Blueprint asset name directly in
type_name. Do not add _C, and do not construct a generated class path ā just use the name
("BP_Enemy", or the full "/Game/Blueprints/BP_Enemy"). Appending _C ("BP_Enemy_C") or guessing
a generated class path is wrong; the type system resolves Blueprint names automatically via asset
search.
Engine structs need the F prefix: color variables are "FLinearColor" / "FColor" ā
plain "LinearColor" / "Color" is not resolved. Unsure of a type string? Use the surviving
unreal.BlueprintService.search_variable_types("Linear") to look it up.
Supported primitive type_name values (exact): bool, int, float, byte, name, string,
text, Vector, Rotator, Transform, Vector2D, LinearColor. Use int (not int32) and
float (not double) ā add_variable rejects int32/double with
"Unknown type 'int32'. Supported: ā¦". For other engine structs (e.g. FColor, FGameplayTag) use
add_struct_variable with struct_type; for object/class refs use add_object_variable with
object_class as a {refPath}:
# Object-reference variable ā object_class is a {refPath} to the class
call_tool("add_object_variable", "editor_toolset.toolsets.blueprint.BlueprintTools",
{"blueprint": {"refPath": "/Game/BP_MyActor.BP_MyActor"}, "name": "DeathFX",
"object_class": {"refPath": "/Script/Niagara.NiagaraSystem"}})
Struct/object/enum types from Python ā add_member_variable (VibeUE delta)
From inside execute_python_code, unreal.BlueprintService.add_member_variable(bp_path, name, type_string, default="", is_array=False, container_type="") creates a member variable with full
type-string support ā structs, objects, enums, and containers ā in one batchable Python call
(no call_tool round-trip). It takes the same type strings as add_function_local_variable
("float", "FVector", "FStateTreeDelegateDispatcher", "AActor"):
unreal.BlueprintService.add_member_variable(bp_path, "FinishRotatingDispatcher", "FStateTreeDelegateDispatcher")
unreal.BlueprintService.add_member_variable(bp_path, "Waypoints", "FVector", "", True) # FVector array
unreal.BlueprintEditorLibrary.compile_blueprint(unreal.EditorAssetLibrary.load_asset(bp_path))
Returns False (with the reason in the log) on a duplicate name or an unresolvable type string.
Compile after adding ā the variable is registered but the class is only rebuilt on compile.
ā ļø Adding a function graph ā engine BlueprintTools.add_function_graph
Creating a function graph moved to the engine toolset (create_function / add_function on
BlueprintService were cut). Use call_tool with blueprint + graph_name:
call_tool(
tool_name="add_function_graph",
toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",
arguments={"blueprint": {"refPath": "/Game/Blueprints/BP_MyActor.BP_MyActor"}, "graph_name": "DoThing"},
)
Refining the graph afterward still uses the surviving VibeUE delta methods
(add_function_parameter, add_function_local_variable, get_function_parameters,
get_function_info, override_function, open_function_graph).
ā ļø Method Name Gotchas
| WRONG | CORRECT |
|---|---|
get_component_info(path, name) | get_component_info(type) - takes ONLY type! |
ā ļø Property Name Gotchas
| WRONG | CORRECT |
|---|---|
info.inputs | info.input_parameters |
var.name | var.variable_name |
ā ļø Python Naming: Boolean b Prefix Is Stripped
UE Python strips the lowercase b prefix from boolean properties. Always use the name without it:
| C++ name | Python name |
|---|---|
bAlreadyOverridden | already_overridden |
bIsEventStyle | is_event_style |
bIsNativeEvent | is_native_event |
bEnabled | enabled |
Do not use b_already_overridden, b_is_event_style, etc. ā these will raise AttributeError.
Exception: the string-based CDO accessors get_property / set_property take the native C++
property name, so booleans KEEP the b prefix there ("bReplicates", not "replicates"). See the
next section.
ā ļø CDO Properties: get_property / set_property (replication, lifespan, etc.)
Use these to read/write Class Default Object properties like replication or initial lifespan:
import unreal
bp = "/Game/Blueprints/TestActor"
# READ ā returns a single value: str, or None if the property wasn't found. NOT a (success, value) tuple!
value = unreal.BlueprintService.get_property(bp, "bReplicates") # "True" / "False" (a STRING)
life = unreal.BlueprintService.get_property(bp, "InitialLifeSpan") # "5.000000"
# WRONG ā raises ValueError: too many values to unpack
# success, value = unreal.BlueprintService.get_property(bp, "bReplicates")
# WRITE ā values are strings; returns bool
unreal.BlueprintService.set_property(bp, "bReplicates", "True")
# Compile via the engine BlueprintTools toolset (compile_blueprint was cut from BlueprintService):
# call_tool(tool_name="compile_blueprint",
# toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",
# arguments={"blueprint": {"refPath": "/Game/Blueprints/TestActor.TestActor"}})
unreal.EditorAssetLibrary.save_asset(bp)
Rules:
- Returns str or None ā UE Python collapses
bool Func(..., Out&)into one return value. This applies to everyget_*_infomethod too (get_variable_info,get_function_info,get_component_info,get_node_details): they return the info struct orNone, never a tuple. - Property names are the native C++ names:
bReplicates,InitialLifeSpan,bCanBeDamagedā thebprefix is NOT stripped here (a stripped name returnsNone). - Values read back as strings: compare
value == "True", notvalue is True.
ā ļø Info Struct Fields (don't guess ā these are the complete lists)
get_blueprint_info(path) ā BlueprintDetailedInfo: blueprint_name, blueprint_path,
parent_class, is_widget_blueprint, variables, functions, components.
| Struct | Fields |
|---|---|
BlueprintFunctionInfo (from list_functions) | function_name, return_type, parameters, is_override, is_pure ā there is no function_type and no input_parameters (that's the Detailed struct) |
BlueprintFunctionDetailedInfo (from get_function_info) | function_name, graph_guid, input_parameters, output_parameters, local_variables, is_override, is_pure, node_count ā there is no return_type; return values are entries in output_parameters (each param: parameter_name, parameter_type, is_output, is_reference, default_value) |
BlueprintVariableInfo | variable_name, variable_type, category, is_public, is_exposed, default_value |
BlueprintVariableDetailedInfo (from get_variable_info) | variable_name, variable_type, type_path, category, default_value, tooltip, is_array, is_set, is_map, is_instance_editable, is_blueprint_read_only, is_expose_on_spawn, is_expose_to_cinematics, is_private, replication_condition ā booleans use the is_ prefix (instance_editable alone raises AttributeError) |
BlueprintLocalVariableInfo (in BlueprintFunctionDetailedInfo.local_variables) | variable_name, friendly_name, variable_type, display_type, default_value, category, guid, is_const, is_reference, is_array, is_set, is_map ā local variables are NOT parameters: there is no parameter_name |
BlueprintPinInfo (from get_node_pins / node.pins) | pin_name, pin_type, is_input, is_connected, default_value ā direction is the single is_input bool; there is no is_output (use not pin.is_input) |
BlueprintNodeTypeInfo (from discover_nodes) | display_name, category, spawner_key, node_class, tooltip, is_pure, is_latent, keywords ā the description text is tooltip, not description |
BlueprintComponentInfo | component_name, component_class, attach_parent, is_root_component, is_scene_component, is_inherited, children |
BlueprintGraphInfo (from list_graphs) | graph_name, graph_kind, node_count |
BlueprintNodeInfo (from get_nodes_in_graph) | node_id, node_type, node_title, pos_x, pos_y, pin_names, pins |
BlueprintCompileResult | success, num_errors, num_warnings, errors, warnings |
ComponentDetailedInfo (from get_component_info) | name, display_name, class_path, category, parent_class, is_scene_component, is_primitive_component, property_count, function_count ā there is no description |
ComponentTypeInfo (from get_available_components) | name, display_name, class_path, category, base_class, is_scene_component, is_primitive_component, is_abstract |
ComponentPropertyInfo (from get_all_component_properties) | property_name, property_type, category, value, is_editable, is_inherited |
Component API quick reference (all on unreal.BlueprintService)
These signatures are complete ā no need to discover_python_class them again:
| Method | Notes |
|---|---|
get_available_components(search_filter="", max_results=50) ā [ComponentTypeInfo] | Discover addable component types by partial name match |
get_component_info(component_type) ā ComponentDetailedInfo or None | Takes ONLY the type name ā no blueprint path |
list_components(bp) / get_component_hierarchy(bp) ā [BlueprintComponentInfo] | Both return the same flat list ā use attach_parent / children / is_root_component fields to render the tree |
component_exists(bp, name) ā bool | Fast idempotency check before adding |
add_component(bp, type, name, parent_name="") ā bool | Compiles inline. See root behavior below |
remove_component(bp, name, remove_children=True) ā bool | Removes children too by default |
reparent_component(bp, name, new_parent) ā bool | |
set_root_component(bp, name) ā bool | New root must be a SceneComponent. Old auto-generated DefaultSceneRoot is removed; an old user-created root and any other root-level scene components become children of the new root |
get_component_property(bp, comp, prop) ā str or None | |
set_component_property(bp, comp, prop, value) ā bool | value is a STRING ā see formats below |
get_all_component_properties(bp, comp, include_inherited=True) ā [ComponentPropertyInfo] | list_component_properties is an alias |
compare_components(bp_a, comp_a, bp_b, comp_b) ā str or None | Diff as text; same or different blueprints |
set_collision_settings(bp, comp, collision_enabled, object_type, collision_profile, channel_responses) ā bool | Collision lives in BodyInstance ā set_component_property cannot reach it |
Root component behavior: components added with no parent_name go to root level. The first
scene component added this way replaces the auto-generated DefaultSceneRoot and becomes the root;
later parentless scene components become floating siblings, NOT children of the root. To build a
hierarchy, pass parent_name when adding, or use reparent_component / set_root_component.
ā ļø Property Value String Formats (set_component_property)
Values are strings in UE export-text syntax. Check property_type via
get_all_component_properties before guessing a struct format:
| Property type | Format example |
|---|---|
float / int32 / bool | "5000.0", "25", "true" |
FVector / FRotator | "(X=0,Y=0,Z=50)", "(Pitch=0,Yaw=90,Roll=0)" |
FColor (e.g. light LightColor) | "(R=255,G=127,B=0,A=255)" ā integer bytes 0ā255 |
FLinearColor | "(R=1.0,G=0.5,B=0.0,A=1.0)" ā floats 0ā1 |
Enum (e.g. IntensityUnits) | "Candelas" |
ā Common mistake: LightColor is FColor, so passing LinearColor-style floats like
"(R=1,G=0.5,B=0)" silently produces a nearly-black light (R=1 of 255), not orange.
ā ļø StateTree Dispatcher Variable Type
To add a StateTree delegate dispatcher variable to a Blueprint task (e.g. STT_*):
# Use type "FStateTreeDelegateDispatcher" ā NOT "EventDispatcher".
# The engine's BlueprintTools.add_variable REJECTS this type ("Unknown type" ā basic types
# only, live-verified); use the VibeUE struct-capable door instead:
unreal.BlueprintService.add_member_variable(bp_path, "FinishRotatingDispatcher", "FStateTreeDelegateDispatcher")
unreal.BlueprintEditorLibrary.compile_blueprint(unreal.EditorAssetLibrary.load_asset(bp_path))
"EventDispatcher" is a Blueprint-only concept and is not a valid type string. The correct type
for StateTree delegate transitions is the USTRUCT FStateTreeDelegateDispatcher.
After adding this variable, use bind_transition_to_delegate on the StateTree to link it to a
transition ā it matches the task by registered class name (STT_Rotate_C), not asset path.
Event Dispatchers (regular Blueprint multicast delegates)
Use the dedicated dispatcher API ā NOT add_variable ā to create the multicast delegates that appear in the Blueprint editor's Event Dispatchers section:
| Method | What it does |
|---|---|
add_event_dispatcher(bp_path, name) | Adds the dispatcher (member variable + signature graph). Skeleton is recompiled inline; the dispatcher is callable immediately. |
add_event_dispatcher_parameter(bp_path, name, param_name, param_type, is_array=False, container_type="") | Adds a parameter to the dispatcher's signature. Subscribers receive these as inputs. |
add_call_delegate_node(bp_path, graph, name, pos_x, pos_y) | Spawns the "Call " broadcast node on a UK2Node_CallDelegate. Wire the broadcast into its execute pin. |
remove_event_dispatcher(bp_path, name) | Removes the dispatcher and its signature graph. |
import unreal
bp = "/Game/StateTree/BP_Cube"
# 1. Create the dispatcher (no compile needed before placing nodes ā skeleton is regenerated inline)
unreal.BlueprintService.add_event_dispatcher(bp, "FinishedLooking")
# 2. (Optional) Add parameters to the signature
unreal.BlueprintService.add_event_dispatcher_parameter(bp, "FinishedLooking", "Direction", "FRotator")
# 3. Spawn the Call node and wire something into its execute pin
call_id = unreal.BlueprintService.add_call_delegate_node(bp, "EventGraph", "FinishedLooking", 1400, -700)
unreal.BlueprintService.connect_nodes(bp, "EventGraph", timeline_id, "Finished", call_id, "execute")
# 4. Compile (engine BlueprintTools toolset) + save
# call_tool(tool_name="compile_blueprint",
# toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools",
# arguments={"blueprint": bp})
unreal.EditorAssetLibrary.save_asset(bp)
Pins on the Call node: execute (input exec), then (output exec), self (input target ā defaulted to Self), plus one input pin per signature parameter.
Subscribing to it elsewhere:
- Preferred ā
add_delegate_bind_on_variable(other_bp, graph, variable_name, "FinishedLooking", x, y)when the dispatcher lives on the class of an existing variable (e.g.Cube : BP_Cube_C). Mirrorsadd_function_call_on_variable: derives the owner class from the variable's type, creates the bind node + Get, and auto-wires Target. Pair withadd_custom_event_nodeand wireCustomEvent.OutputDelegate ā Bind.Delegate. - Lower-level ā
add_delegate_bind_node(other_bp, graph, "<owner class>", "FinishedLooking", x, y)when you have no variable to derive from.<owner class>accepts"Self", native class names (with/without U/A prefix), Blueprint asset paths (/Game/.../BP_Cube), or short BP names with/without_C(BP_Cube,BP_Cube_C). You wire the Target pin yourself.
Common mistakes:
- ā
add_variable(bp, "FinishedLooking", "EventDispatcher")ā"EventDispatcher"is not a real type string; useadd_event_dispatcherinstead. - ā Treating the dispatcher like a regular variable (
add_get_variable_nodeon it produces a delegate-getter, not a broadcast). The broadcast node isUK2Node_CallDelegateand is created only byadd_call_delegate_node. - ā Skipping the compile before saving the asset. The skeleton compiles inline on dispatcher creation, but the final asset still needs the engine
BlueprintTools.compile_blueprinttoolset call beforesave_assetto ensure the generated class is up-to-date.
Blueprint Interfaces (implement / remove)
Add or remove a Blueprint Interface on a Blueprint class ā the equivalent of Class Settings ā Interfaces ā Add in the editor.
| Method | What it does |
|---|---|
add_interface(bp_path, interface) | Implements the interface on the Blueprint. No-op (returns True) if it is already implemented. Marks the Blueprint structurally modified and recompiles it inline. |
remove_interface(bp_path, interface) | Removes the interface from the Blueprint. Marks structurally modified and recompiles inline. |
import unreal
bp = "/Game/Blueprints/BP_Player"
# Prefer the FULL interface asset path ā most reliable resolution
unreal.BlueprintService.add_interface(bp, "/Game/Interfaces/BPI_Interactable")
# Short name also works IF the interface asset is already loaded in the editor
unreal.BlueprintService.add_interface(bp, "BPI_Interactable")
# Persist (both methods compile inline, but still save the asset)
unreal.EditorAssetLibrary.save_asset(bp)
# Remove it later
unreal.BlueprintService.remove_interface(bp, "BPI_Interactable")
unreal.EditorAssetLibrary.save_asset(bp)
Notes & gotchas:
- Prefer the full asset path (e.g.
/Game/Interfaces/BPI_Interactable). Short-name resolution only finds interface Blueprints that are already loaded in the editor; a full path always loads and resolves. - Both methods recompile the Blueprint inline (
CompileBlueprint) but do not save it ā callEditorAssetLibrary.save_asset(bp)afterward to persist. add_interfaceis idempotent ā calling it for an interface that is already implemented returnsTruewithout duplicating it.- After adding an interface, its functions appear as overridable events/functions; use
override_function(bp_path, function_name)to implement them in the graph. - Both return
bool(Trueon success). AFalsefromadd_interfacemeans either the interface path could not be resolved, or it resolved to an asset that is not a Blueprint Interface (e.g. a normal Blueprint or one merely parented toUInterface) ā check the log and pass a real Blueprint Interface asset path. - The interface must be a true Blueprint Interface (
BPTYPE_Interface), created withBlueprintInterfaceFactory.add_interfacevalidates this and returnsFalsefor non-interface classes rather than letting the compile fail.
Task Index
| Task | Workflow | Sample script (run via execute_python_code) |
|---|---|---|
| Create a Blueprint + variables | workflows.md ā Create a Blueprint | scripts/create_blueprint.txt |
| Add components + set properties | workflows.md ā Add components | scripts/add_component.txt |
| Add an event dispatcher (delegate) | workflows.md ā Event dispatcher | scripts/event_dispatcher.txt |
| Implement a Blueprint interface | workflows.md ā Implement an interface | scripts/add_interface.txt |
| Inspect a Blueprint (components, parent class) | introspection.md | ā |
| Node-level graph editing | load the blueprint-graphs skill | ā |
Sub-docs
workflows.mdā step-by-step create/component/dispatcher/interface workflows with copy-paste Python.introspection.mdā what Blueprint introspection works/doesn't in UE 5.7 (CDO reads, parent class via asset tags, listing BPs by class, getting level actors).
Verification
After any edit: compile via the engine BlueprintTools.compile_blueprint toolset call
(call_tool(tool_name="compile_blueprint", toolset_name="editor_toolset.toolsets.blueprint.BlueprintTools", arguments={"blueprint": path}))
and check the result's success / num_errors, then unreal.EditorAssetLibrary.save_asset(path).
Don't claim success until compile reports zero errors.