Blueprint Exporter plugin — user guide
Blueprint Exporter is an Unreal Editor plugin. It writes Blueprints to text files that the BlueprintReview tools (blueprintreview, blueprintreview-app) read, and that can be diffed, reviewed or archived outside the editor. For each Blueprint it writes:
<Blueprint>.json: everything in the Blueprint, in the same format asblueprintreview dump:- variables, components and class defaults;
- the enums the Blueprint uses, with each entry's display name;
- every graph with its nodes, pins and wires;
- the node titles, colours and pin labels exactly as the editor shows them.
<Blueprint>.<Graph>.t3d: each graph in the editor's own copy/paste format. Paste it back into a Blueprint graph, or into blueprintue.com.
You don't need the plugin to view Blueprints: blueprintreview reads .uasset files directly. The plugin is for:
- Exact editor output: node titles, colours and pin labels as the editor computes them.
blueprintreviewreads graphs, nodes and pins, but not that display text. - Batch export: exporting a whole project from the command line, e.g. on a build machine.
- Checking
blueprintreview: comparing its reading of an asset against the editor's (blueprintreview check).
Editing Blueprints (by hand, by script or by an AI agent) is the companion plugin's job,
Blueprint Edit Automation (plugins/BlueprintEditAutomation): see EDITING.md. It's experimental, and it needs
this plugin enabled.
Supported engines: UE 5.7 and 5.8 are tested. The code is written to also build on 4.27 and 5.0–5.6, but those haven't been tried yet. The plugin depends on the engine's Enhanced Input plugin (enabled by default in UE 5 projects) and enables it.
1. Install it into a project
The plugin source is in this repository at plugins/BlueprintExporter.
C++ projects (the project has a Source/ folder)
- Close the editor.
- Copy
plugins/BlueprintExporterto<YourProject>/Plugins/BlueprintExporter. - Open the project. The editor offers to build the missing module; click Yes. You can also build from your IDE.
Blueprint-only projects
The editor can't compile plugins for a project without C++ code, so use a prebuilt copy for your engine version. Someone with Visual Studio installed builds it once:
"C:\Program Files\Epic Games\UE_5.7\Engine\Build\BatchFiles\RunUAT.bat" BuildPlugin ^
-Plugin="<repo>\plugins\BlueprintExporter\BlueprintExporter.uplugin" ^
-Package="D:\Build\BlueprintExporter_5.7" -TargetPlatforms=Win64 -Rocket
Then copy D:\Build\BlueprintExporter_5.7 to <YourProject>/Plugins/BlueprintExporter, leaving out its HostProject and Intermediate folders. A build only works with the engine version it was built for.
With the deploy script (from a checkout of this repo)
scripts/deploy_plugin.py installs the plugin and the converter from your checkout in one go. Use it to try the current code in a real project, or to update a project after pulling. Close the editor for that project first, then run:
python scripts/deploy_plugin.py "D:/Documents/Unreal Projects/StackOBot"
It needs Rust (cargo) and, for a Blueprint-only project, Visual Studio. It takes the project folder or its .uproject, and it:
- builds
blueprintreview(cargo build --release -p blueprintreview-cli); - installs the plugin to
<Project>/Plugins/BlueprintExporter. A C++ project gets the source, and the editor builds it when the project opens. A Blueprint-only project gets aRunUAT BuildPluginbuild for the engine in the.uproject'sEngineAssociation(C:/Program Files/Epic Games/UE_<version>). If a copy is already installed, it's replaced; - copies
blueprintreview.exeinto the plugin'sTools/folder; - enables
BlueprintExporterin the.uprojectif it isn't enabled yet; - points Project Settings → Plugins → Blueprint Review → Converter Path at the copied executable. This is a per-user setting, written to the project's
Saved/Config/WindowsEditor/EditorPerProjectUserSettings.ini, so nothing machine-specific goes into the project'sConfig/.
The project then has its own copy of everything. Rebuilding in the repo doesn't affect it until you deploy again.
| Option | Effect |
|---|---|
--engine DIR |
Use this engine install, not the one named by EngineAssociation (needed for a source-built engine). |
--prebuilt |
Build the plugin with RunUAT even for a C++ project, so the editor doesn't have to compile it. |
--build-dir DIR |
Where RunUAT builds (default E:/tmp/bpexporter/deploy/UE<version>; the build log goes next to it). |
--skip-cargo |
Copy the existing target/release/blueprintreview.exe without rebuilding it. |
--no-cli |
Install only the plugin. The converter and its setting are left as they are. |
If the editor still has the plugin loaded, the script stops with "…is in use; close the editor and run again", and the installed copy is left as it was. A project that has gained a Source/ folder since the last deploy (after a Convert to C++…, for example) gets the source copy next time.
Check it's enabled
In the editor, open Edit → Plugins and search for "Blueprint Exporter". It's under the Editor category. Tick Enabled if it isn't already, and restart when asked.
The plugin is editor-only. It isn't included in packaged games.
2. Export from the editor
- In the Content Browser, select one or more Blueprints. Actor, Widget, Anim and other Blueprint types all work, and you can select across folders.
- Right-click, then choose Export Blueprint (JSON + T3D)….
- Pick an output folder.
A message lists how many were exported and any that failed. Files are placed under the output folder by their content path:
<output>/Game/Blueprints/BP_Door.json
<output>/Game/Blueprints/BP_Door.EventGraph.t3d
<output>/Game/Blueprints/BP_Door.UserConstructionScript.t3d
<output>/Game/Blueprints/BP_Door.OpenDoor.t3d (one per function/macro graph with nodes)
Unsaved changes in the editor are included, because the plugin exports what's currently loaded, not the file on disk.
3. Export from the command line
Use the editor's command-line executable with the BlueprintExport commandlet. No editor window opens. This suits build machines, scripts and large projects.
"C:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe" ^
"D:\Projects\MyGame\MyGame.uproject" ^
-run=BlueprintExport -Out="D:\Exports" -Paths=/Game/Blueprints ^
-unattended -nullrhi -nosplash
| Argument | Meaning |
|---|---|
-Out=<folder> |
Where to write the files (required). |
-Paths=/Game/A,/Game/B |
Export every Blueprint under these content folders, including subfolders. -Paths=/Game exports the whole project. |
-Assets=/Game/A/BP_X,/Game/B/BP_Y |
Export specific Blueprints, by content path. |
-NoT3D |
Only write the .json files. |
-SaveBeforeExport |
Re-save each Blueprint before exporting it. This overwrites the .uasset files. Use it only to verify blueprintreview, so the file and the export describe exactly the same state (see section 5). |
-Paths and -Assets can be combined. -unattended -nullrhi -nosplash are standard UE flags: no dialogs, no GPU, no splash screen.
The log ends with a summary like:
LogBlueprintExport: Display: BlueprintExport: 48 exported, 0 failed -> D:/Exports
The exit code isn't a reliable success signal. UE returns a non-zero exit code whenever any error was logged during the run. Blueprint compile errors count, and they often appear for assets that are broken or missing references, even though they still export fine. In scripts, check the
0 failedpart of the summary line. A Blueprint that can't be exported at all is logged asLogBlueprintExport: Error: <path>: ....
4. What's in the files
JSON
The format is described by schema/blueprint.schema.json, and its fields are explained in docs/CLI.md. A node from an export looks like this:
{
"guid": "0ECDACC94AFCBF069FBCF3A860A8E683",
"class": "K2Node_CallFunction",
"pos_x": -128,
"pos_y": 1520,
"title": "Get Actor Up Vector\nTarget is Actor",
"title_color": [0.4, 0.85, 0.35, 1],
"compact_title": "×",
"properties": [
{ "name": "FunctionReference", "type": "StructProperty(MemberReference)", "value": { "Struct": { "...": "..." } } }
],
"pins": [
{
"id": "5983533840253B1DBC9250803918DAED",
"name": "self",
"direction": "input",
"friendly_name": "Target",
"type": { "category": "object", "sub_category_object": "/Script/Engine.Actor" },
"display_name": "Target"
}
]
}
The editor-only fields:
titleandtitle_color: what the editor draws in the node header.compact_title: the text drawn in the body of nodes the editor draws compact (×,+,GET). Only those nodes have one.display_name: the pin label the editor shows, when it differs from the pin's name.""means the pin is drawn without a label, like exec pins.compiled_flags(on variables): thePropertyFlagsof the compiled skeleton class property, which the Details panel lists as Defined Property Flags. These are the storedproperty_flagsplus the flags the compiler and the type add. A.uassetdoesn't save the type-implied ones (FProperty::SerializedropsCPF_ComputedFlags), so the viewer derives them when this field is missing.
friendly_name is the pin's saved label, so the standalone parser reads it too.
The files are designed to diff cleanly in source control:
- Nodes are sorted by their GUID.
- Wires are written as
"linked_to": ["<nodeGuid>:<pinId>"]. - Nothing changes between exports unless the Blueprint changed.
- Only values that differ from their defaults are listed (class defaults, class settings, component settings, node settings), the same as what UE saves in the
.uasset.class_settingsholds theUBlueprint's own saved properties (Display Name, Hide Categories, Deprecate, ...) except those other fields carry or that are generated (bp_model::BLUEPRINT_PROPERTIES_EXCLUDED).
T3D
Each .t3d file is exactly what Ctrl+C on that graph's nodes would put on the clipboard. blueprintreview copy and the viewer's Ctrl+C write the same format without the editor. To use one:
- Back into the editor: open the file, copy all of it, and press Ctrl+V in any Blueprint graph.
- Sharing: paste it into blueprintue.com.
5. Checking blueprintreview against the editor
blueprintreview check compares blueprintreview's own reading of a .uasset with the plugin's export of it:
blueprintreview check Content/Blueprints/BP_Door.uasset D:/Exports/Game/Blueprints/BP_Door.json
For the result to be meaningful:
- Export in the asset's own project, so every class and asset it references can load. Missing references load as
None. - Export with the engine version that last saved the asset. A newer engine may consider different values "default". It also upgrades data when loading, so its view differs from the saved file.
Export assets checked this way can be added to the test fixtures as golden files (see fixtures/README.md).
Checking blueprintreview copy against the editor
-ImportT3D=<list> pastes copy text into Blueprints and exports what was pasted. Each line of the list file has four tab-separated fields: the Blueprint (/Game/A/BP_X), the graph name, the copy text file, and the JSON file to write. The text is pasted with FEdGraphUtilities::ImportNodesFromText, the editor's paste, into a new graph that temporarily takes that graph's name and place. Pasted nodes keep their GUIDs.
UnrealEditor-Cmd.exe MyGame.uproject -run=BlueprintExport -ImportT3D=D:/paste.txt -unattended -nullrhi
scripts/plugin_roundtrip.py --t3d uses it to check blueprintreview copy. For every graph it pastes blueprintreview's copy text and the editor's own (the plugin's .t3d), each in a fresh editor session, and compares the two pasted graphs with blueprintreview check --graph. Pasting changes nodes (they're rebuilt, and names made unique), so the two texts are compared by what they paste to, not by what they say.
6. Engine metadata for the viewer
A .uasset doesn't contain the display names of the engine functions it calls. For example, K2_GetActorLocation is shown as "Get Actor Location". It doesn't contain a variable's type-implied property flags, engine enums' display names, or the values a Blueprint inherits from its native parent classes either. The viewer gets them from two files built from the engine: a table, crates/bp-model/data/engine_table.tsv, and the class defaults, crates/bp-model/data/engine_defaults.json.
To regenerate the table after an engine upgrade:
- Build the plugin for each engine you want covered.
- Run the
BlueprintMetadataDumpcommandlet once per engine, oldest first, and merge the results:
python scripts/gen_engine_table.py \
--run "C:/Program Files/Epic Games/UE_5.7" D:/Work/RoundTrip57/RoundTrip.uproject \
--run "C:/Program Files/Epic Games/UE_5.8" D:/Work/RoundTrip58/RoundTrip.uproject
Any project with the plugin enabled works, for example the work projects of scripts/plugin_roundtrip.py. To run the commandlet by hand:
UnrealEditor-Cmd.exe MyGame.uproject -run=BlueprintMetadataDump -Out=D:\engine_table.tsv -Defaults=D:\engine_defaults.json
The table covers every Blueprint-visible function, class (with its DefaultToInstanced / Interface flags and the interfaces it implements), struct display name, native enum entry display name (where it isn't the entry's own name), StandardMacros entry and input key name in the modules the editor loads. It also has the compiled flags of a variable of each struct and each other type kind. The commandlet gets them by creating the variable's property with the Kismet compiler's own code (FKismetCompilerUtilities::CreatePropertyOnScope) and linking it. It's sorted and has no timestamps, so regenerating it with the same engine gives the same file.
-Defaults= also writes the class defaults: for every class a Blueprint can derive from and every component class (with their ancestors), its parent, its editable properties and its default subobjects (Character's capsule, mesh and movement component). A class lists the properties it declares with their whole value and Category, and the inherited ones its constructor changed as a delta from its parent's, so the file stays small (about 2 MB, 150 KB compressed). gen_engine_table.py keeps the newest engine's; the viewer's Class Defaults says which engine that is when the asset was saved by another.
7. The engine API table for the C++ converter
The Blueprint → C++ converter (blueprintreview cpp) generates code against an API table: for every native class, struct and enum its C++ name, header and module, for every Blueprint-visible function its native signature, default arguments and metadata, for every Blueprint-visible property its C++ type and access. One table per engine version is committed as crates/bp-cpp/data/engine_api_<version>.tsv.
To regenerate them (this builds the plugin for each engine and runs the commandlet in a scratch project with the engine plugins the fixtures use enabled):
python scripts/gen_engine_api.py --engine "C:/Program Files/Epic Games/UE_5.7" --engine "C:/Program Files/Epic Games/UE_5.8"
A project's own table, for its C++ and its project plugins (blueprintreview cpp --project-api), from a project that has the plugin installed:
python scripts/gen_engine_api.py --engine "C:/Program Files/Epic Games/UE_5.7" --project D:/Work/MyGame/MyGame.uproject --out MyGame.api.tsv
By hand: UnrealEditor-Cmd.exe MyGame.uproject -run=BlueprintMetadataDump -Api=D:\engine_api.tsv [-ProjectOnly].
Reflection doesn't record whether a function is defined inline, which makes it callable from other modules without being exported. The commandlet reads the class's header (the engine's installed source) to find out, and the same way finds the inline GetFoo() getters of private members such as ACharacter::GetCharacterMovement. The table also lists the BlueprintAssignable dispatchers (M) with their signatures (Y rows), so the converter can declare bound functions exactly as AddDynamic checks them. It also marks the members a class's header declares under UE_DEPRECATED (C, as USkinnedMeshComponent::ForcedLodModel): Blueprints still use them directly, but C++ has to go through their accessors. It also flags a MinimalAPI class whose header declares a virtual function that is neither exported nor inline (UEnvQueryContext_BlueprintBase::ProvideContext): a Blueprint can subclass it, but a C++ class in another module can't link. Editor-only properties (WITH_EDITORONLY_DATA, such as USplineComponent::EditorTangentColor) are marked W, so the converter sets them under the same guard and a game build still compiles. A native setter must take the value: UShapeComponent::SetUseSystemDefaultObstacleAreaClass() takes none, so it isn't recorded as bUseSystemDefaultObstacleAreaClass's setter. The committed tables predate both rules; the converter keeps a short list of the affected members until they're regenerated.
A Blueprint-only project that enables engine plugins the committed tables don't include can get a full table without installing the plugin into it: --plugins-of MyGame.uproject --out MyGame.api.tsv makes a scratch project with the same engine plugins enabled.
Test Blueprints for the converter
-run=BlueprintTestContent builds the converter's verification Blueprints (tests/cpp-project/Content/CppTests) from code: a user enum and struct, a Blueprint Interface, an actor with a variable of every kind, one with a component tree, one with function signatures, a dispatcher and the interface, a function library, a Blueprint child, a UObject and an actor component Blueprint, BP_Logic, whose functions have bodies (arithmetic, branches, a sequence, loops, a switch, a cast, IsValid, calls), BP_Events, with an event graph (BeginPlay, Tick, custom events, a Server RPC, an event parameter another event reads, FlipFlop and DoOnce), BP_Delegates (a dispatcher bound in BeginPlay, broadcast and unbound by custom events, and a component's overlap event), BP_Latent (a delay whose continuation reads a value written before it, a next-tick delay, and a retriggerable delay a custom event restarts), BP_Timeline (a timeline with a float and an event track, played from BeginPlay), and BP_Input (a pawn with two Enhanced Input actions, IA_CppTestThrottle and IA_CppTestFire, made as assets too: Triggered, Started and Completed events, a value Tick reads, and Get Input Action Value). -Only=BP_Variables,... makes some of them.
-run=BlueprintBehaviour -Map=<folder> -Module=<module> [-Actors] compares every pure function of the Blueprints converted into that folder with its C++ version: both are called on their class default objects with the same sampled inputs, and every output is compared. With -Actors, each actor class also runs in both forms in a game world. Both forms are spawned, ticked 5 frames, have each custom event called with the same sampled arguments, and are ticked twice more. Then every variable is compared. The C++ form is spawned deferred, so the members the .bpmap.json lists as kept can be set to their assets before its construction and BeginPlay, as the reparented Blueprint's defaults would set them. Pawns and player controllers also get an Enhanced Input component through the engine's own setup path (SetupPlayerInputComponent then the Blueprint's input bindings, or SetupInputComponent). Both forms must bind the same actions, trigger events and values. Each event binding then runs in both forms with the same sampled action instances, before the last ticks. scripts/cpp_compile_check.py --behaviour runs it after the build. scripts/cpp_compile_check.py --make-content runs it and copies the assets into the repo; see tests/cpp-project/README.md.
Each actor runs in a test world of its own, after a twin of the same form, so overlap and actor-query logic finds one. The values are compared in a form both have: enums by value, structs field by field in order, object references relative to the actor, numbers within a relative 1e-5. Across a migration, the two forms are in two projects (or one project before and after):
-Record=<file.json>runs the original Blueprints and writes what they did, under the Blueprints' names.-Compare=<file.json>runs the migrated forms against that file. Those are the reparented Blueprint (same class, the C++ names inherited) or the class that replaced it.
Migrating a project: BlueprintNativize
-run=BlueprintNativize -Map=<folder of .bpmap.json> -Step=Types|Classes [-KeepReplaced] [-DryRun] applies the converter's output to the project. See CLI.md for what each step does. Run it on a copy or under source control: it deletes the replaced assets and resaves the Blueprints and maps that reference them. Its summary lines are:
Nativize types: N asset(s) replaced, M error(s);Nativized: <asset>: …per Blueprint, each with its compiler messages;Nativize: N asset(s), R referencer(s), S saved, E error(s), W warning(s).
A referencer's message counts only when its baseline (Nativize.baseline.json, written by the Types step) didn't have it.
With -KeepReplaced (pass it to both steps), the replaced assets aren't deleted: they stay at their path, unused. Their redirects send every reference to the C++ type, and after the resave Classes checks the asset registry: each kept asset that a project package (other than another kept one) still references is a warning, Kept: <asset> is still referenced by …, and the exit code is 1. A kept asset is a copy for reference only. Loading it by its path gives the C++ type (the redirect), so don't place, use or edit it. Delete it once you no longer need it.
8. Convert to C++ from the editor
Right-click Blueprints, user-defined structs or enums in the Content Browser and choose Convert to C++…. This does what CONVERTING.md describes, from one dialog:
| Option | What it does |
|---|---|
| Mode | Native base reparents each Blueprint onto its C++ class. Replace lets the C++ class take over. In Replace, a class only its Blueprint can keep parts of takes a native base instead, by itself. |
| Replaced Blueprints | Keep at their path (the default) or Delete. This applies to what's replaced: the structs, enums, Blueprint Interfaces and function libraries in either mode, and every class in Replace. A kept asset is unused afterwards (see -KeepReplaced above). |
| Runtime errors | As Blueprint (--safety bp) or Idiomatic C++. |
| Module and Path | Where the code goes, as in New C++ Class. Pick one of the project's modules (or a project plugin's), and a folder inside it, by default <Module>/Converted. The header and source of each type go into that one folder, since the generated files include each other by name. For a Blueprint-only project, the list shows the project's name as a new module, which is created. |
| Move only part of the Blueprint | The partial migration's --functions, --variables, --events, --keep-in-bp and --pull-dependencies, native base only. |
The dialog checks when it opens: it lists each asset as ready or blocked, with the blockers. Blueprints the selection depends on are listed as (dependency) and converted with it. Tick that the project is committed or backed up, then Convert:
-
Check, then generate. It checks again with the chosen options, then generates the code with
blueprintreview cppagainst an API table of everything the editor has loaded (Saved/BlueprintReview/api.tsv), which covers the project's C++ and its plugins. Writing that table reads the header of every native class, so it shows its progress and can be cancelled. It's kept with what it was made from (api.tsv.key: the engine version and every loaded module's binary) and reused until a module is rebuilt, added or removed. While the converter runs, the dialog shows what it's doing (--progress) with a progress bar: reading the assets, loading what they use, checking, generating each asset, writing the files, and in replace mode reading every package of the project to find what references the replaced assets. -
Confirm. It shows what the converter printed (the values left to the Blueprints, the asset members) and asks before going on. If you stop there, the code is written and nothing else has changed.
-
Set up the project:
- The module's
Build.csgets a marked block (// BEGIN blueprintreview: Converted…// END …) with the folder's include path and itsModules.txt. Converting into the same folder again replaces the block. - The
.uprojectenables the plugins inPlugins.txt. In a Blueprint-only project it also gets the new module, created with itsBuild.cs, its.cppand the game and editor targets.
- The module's
-
Save and close. It saves the open assets (asking first), starts the runner (
Saved/BlueprintReview/Convert/<time>/run.bat) and closes the editor. -
The runner, in a console window:
- waits for the editor to exit;
- builds the editor target (
build.log); - runs
BlueprintNativize -Step=Types(types.log), then-Step=Classes(classes.log), with-KeepReplacedunless you chose Delete; - opens the editor again, which shows the outcome in a notification with an Open log link.
If the build fails, nothing is migrated. The runner shows
build.loginstead of opening the editor, since the project's module doesn't build. To go back, delete the output folder and its block inBuild.cs.
The converter is found through Project Settings → Plugins → Blueprint Review → Converter Path, or as blueprintreview on PATH when that's empty. scripts/deploy_plugin.py sets it (section 1). The dialog remembers its choices per user (EditorPerProjectUserSettings). Windows only, like the rest of the plugin's build steps.
The game trace: BlueprintTrace
The plugin's second module, BlueprintTrace (uncooked only: it loads in -game from the editor binary and in PIE), records the actors of the converted classes while the game runs. scripts/trace_diff.py diffs two recordings:
UnrealEditor.exe MyGame.uproject /Game/Maps/Level1 -game -nullrhi -deterministic -benchmark -fps=30 -BlueprintTrace=E:/tmp/trace.json -TraceMap=Source/MyGame/Converted [-TraceFrames=600] [-TraceEvery=15] [-TraceInput=input.json]
- What is recorded: every
-TraceEveryframes, each actor whose class is a converted asset in any of its forms. Each sample has its location, rotation,hidden, and its variables under their Blueprint names (-TraceMapholds the manifests). After-TraceFramesframes the file is written and the game exits. - Keys: level actors are keyed by level and name, spawned ones by asset and spawn order.
- Input:
-TraceInputinjects Enhanced Input actions on given frames,{ "events": [ { "frame": 30, "frames": 5, "action": "/Game/Input/IA_Move.IA_Move", "value": [1, 0, 0] } ] }. -TraceAll: traces every other Blueprint actor too, with the variables its Blueprints declare. That covers what uses the converted types without being converted itself.-TraceFlushLoads: finishes asynchronous loads at the start of every frame, so a load completes in the same frame in every run. Without it, disk and cache timing decide.- In PIE:
BlueprintTrace.Start <out.json>on the console traces the session (the frame settings come from the command line).
Troubleshooting
| Problem | Fix |
|---|---|
| The menu entry is missing | Check the plugin is enabled (Edit → Plugins). The entry only appears when the selection includes a Blueprint. |
| "The following modules are missing or built with a different engine version" | A prebuilt copy for another engine version is installed. Rebuild for your engine, or let a C++ project compile it. |
| Blueprint-only project can't build the plugin | Use a prebuilt copy (section 1). |
LogBlueprintExport: Error: ...: not found or not a Blueprint |
Check the path in -Assets. It's the content path (/Game/Folder/BP_Name), not a file path. |
| Exit code is 1 but everything exported | Expected when other errors were logged; see section 3. |
Some values show as null / None |
The referenced asset or class didn't load. It's missing from the project, or provided by a plugin that isn't enabled. |