🧠 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
⚠️ Components added with add_component are NOT on the CDO
add_component creates a SCS node template, not an instance on the class default object. So the
obvious way to configure it silently does nothing — get_components_by_class on the CDO returns an
empty list and the loop body never runs:
python# WRONG - prints [], the mesh is never assigned, and nothing errors cdo = unreal.get_default_object(bp.generated_class()) for c in cdo.get_components_by_class(unreal.SkeletalMeshComponent): c.set_editor_property("skeletal_mesh_asset", mesh) # CORRECT - go through the service, which edits the SCS template BS.set_component_property(BP, "Windmill", "SkeletalMeshAsset", "/Game/X/SK_Windmill.SK_Windmill") BS.set_component_property(BP, "Windmill", "AnimationMode", "AnimationBlueprint") BS.set_component_property(BP, "Windmill", "AnimClass", "/Game/X/ABP_Windmill.ABP_Windmill_C")
Values are strings: an asset takes its object path (/Game/X/SK.SK), a class takes the
_C path, an enum takes the entry name. Read back with get_component_property to confirm — it
returns the resolved object, e.g.
/Script/Engine.SkeletalMesh'/Game/X/SK_Windmill.SK_Windmill'. Components that come from the
parent C++ class are on the CDO and can be set directly; only SCS-added ones need the service.
Level Blueprints: pass the MAP path
Every unreal.BlueprintService.* function that takes a blueprint_path also accepts a map/world
path (e.g. /Game/Maps/MainMenu) and resolves it to that level's Level Blueprint — inspect and
edit level scripts exactly like normal Blueprints. Explicit subobject paths
(/Game/Maps/MyMap.MyMap:PersistentLevel.MyMap) work too. Compile still goes through
unreal.BlueprintEditorLibrary.compile_blueprint(...) on the returned/loaded level script, and the
MAP asset is what you save afterwards (unreal.EditorAssetLibrary.save_asset("/Game/Maps/MyMap")).
⚠️ 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:
python# (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):
python# 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):
python# 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}:
python# 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"):
pythonunreal.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.
Removing one variable — remove_member_variable (VibeUE delta)
unreal.BlueprintService.remove_member_variable(bp_path, name, force=False) removes exactly one
member variable. The engine alternative, BlueprintEditorLibrary.remove_unused_variables, sweeps
every unreferenced variable — use this when you want to delete just one. It counts Get/Set
references across all graphs first: with references present and force=False it refuses (returns
False, logs the referencing graphs and node counts); with force=True it removes the variable
and its referencing nodes. A component name (from the Simple Construction Script) is refused with a
pointer to the component API. Verifies by readback and returns True only when the variable is gone.
pythonunreal.BlueprintService.remove_member_variable(bp_path, "UnusedScratch") # refuses if referenced unreal.BlueprintService.remove_member_variable(bp_path, "OldHealth", True) # force: nodes go too
⚠️ 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:
pythoncall_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:
pythonimport 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. - On UE 5.3+,
set_propertymaps the deprecated GameplayEffect CDO fieldsInheritableGameplayEffectTagsandInheritableOwnedTagsContainerto their Asset Tags and Target Tags GameplayEffect Components. Writing only the legacy fields is otherwise erased byUGameplayEffect::PreSave.
⚠️ 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_*):
python# 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. |
pythonimport 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. |
pythonimport 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.
Authoring the interface itself. A Blueprint Interface is nothing but its function graphs, so an
empty one contributes nothing when implemented. Use create_function_graph() to define it — it is
the counterpart to override_function(), which can only override a function that already exists:
pythonBS = unreal.BlueprintService BS.create_function_graph(bpi_path, "Interact") # void -> an event on implementers BS.create_function_graph(bpi_path, "GetDisplayName") # returns the graph name, "" on failure BS.add_function_parameter(bpi_path, "GetDisplayName", "Name", "string", True) # is_output=True -> a return value BS.add_interface(bp_path, bpi_path) BS.override_function(bp_path, "Interact") # void interface fn -> event node in the EventGraph
create_function_graph() also works on a normal Blueprint (pass is_pure=True for a pure function).
It returns "" and logs the reason for: an empty or non-identifier name, a name already used by any
graph on the Blueprint, or a name that already exists on the parent hierarchy — that last case is
override_function()'s job, and a shadowing graph would be a duplicate-function compile error.
A return-valued interface function is materialised as a graph on the implementer and shows up in
list_graphs() as Interface (<InterfaceClass>).
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.
Additional gotchas
SubobjectDataSubsystemis an ENGINE subsystem (get_engine_subsystem);get_object_for_blueprintlives onSubobjectDataBlueprintFunctionLibrary; SCS templates are named<Name>_GEN_VARIABLE; aSubobjectDataHandlehas nois_valid()— testk2_find_subobject_data_from_handle(h) is not None.set_collision_enabled(NO_COLLISION)on a spawned actor's component does not survive save/reload;set_collision_profile_name("NoCollision")serialises, andcan_ever_affect_navigation=Falsepersists.- Engine collision profiles predate custom trace channels, so a channel with no explicit entry falls back to the ini default response (BLOCK); a scripted volume or primitive must set each custom channel's response explicitly.


