The last entry ended on the task I expected to break things: something that can’t be done in one tool call and can’t be verified by counting actors. What I got is a playable level, and the thing that broke wasn’t the part I was watching.

A platformer, on purpose
This prototype is shaped like Sly Cooper: run a route, collect the things, get into the vault. The genre is the instrument — each one picks whatever shape makes the next limit easy to see, and a platformer is good for this because I know what a jump should feel like. When the jump is wrong I don’t need to reason about it. What I’m measuring is the agent.
What exists now: one level, about 75 metres end to end, five sections — a courtyard, a staircase of floating platforms, two rails to cross, a gap that needs a dash, and the vault roof. 36 actors. Five greybox props authored by a headless Blender script and imported through MCP. Data chips that count, crates that break and drop a chip, a vault door that sinks into the roof once you’re carrying ten chips. A robot built from sixteen static meshes hung off the mannequin’s bones, so it inherits every animation the engine template ships without a single weight being painted.
The layout is a Python list of coordinates, and the builder deletes everything it made and lays it down again. Iterating on the level is editing numbers. That part of this I’d keep for any genre.
Blueprints as text
The interesting tool in Epic’s plugin is write_graph_dsl: Blueprint graphs as a Lisp.
(event Collision|EventActorBeginOverlap (OtherActor)
(bind rook (Utilities|Casting|CastToBP_ZMG_Rook OtherActor)
(:then
(bind held (Class|BPZMGRook|GetChipsHeld rook))
(Class|BPZMGRook|SetChipsHeld :self rook :ChipsHeld (+ held 1))
(Actor|DestroyActor))
(:CastFailed)))
That is a collectible. All five gameplay Blueprints in this level were written that way, and it changes the arithmetic of a session: logic gets written instead of wired, and it goes into version control as something I can read a month from now.
Two things about it worth knowing before you rely on it.
Writes merge per event. Writing a new event into a graph that already has others leaves them intact. That makes it safe to extend a Blueprint you inherited from a template, which matters because of the other thing:
The round trip is broken. read_graph_dsl emits type ids that write_graph_dsl refuses. Read the engine’s damageable-box Blueprint and you get Math|Vector|vector*vector and (|FadeTimeline 0.0); feed either one back and it doesn’t exist. A duplicated Blueprint reports its own custom event under the name of the class it was copied from, and that name compiles to a node demanding a target it can’t have. So the DSL reads and the DSL writes, and its own output is not valid input. You can extend a graph you didn’t write. You cannot round-trip one.
The tool picked the mechanic
Here’s the part I didn’t expect.
The plan was a melee attack. The engine ships a combat template with a full combo — animation montages, damage interface, breakable boxes — so the character was going to be that one, plus a jump.
The combat template maps no jump. Fine: add the input event for it. Except write_graph_dsl cannot create Enhanced Input events. The registry lists 52 event types you can add to a graph and not one of them is an input action; the ones already in the template’s graph got there through the editor GUI, and there is no tool call that produces another.
So the character became the platforming template instead, which ships jump, double jump and dash already wired — and the melee combo went in the bin, because the button for it can’t be created. Crates now break when you land on them or dash into them: the crate measures the speed of whatever hit it, so it needs no input of its own and no knowledge of the player class.
Landing on things instead of hitting them is arguably the better platformer verb. That’s not why it’s in the game. It’s in the game because of a gap in a schema generator in an experimental plugin, and I want to be precise about that, because “the tool shaped the design” is the kind of thing that’s easy to say about engines in general and boring when it stays general. This was specific. One missing node type moved the attack from a button to a body.
The whole gap is about thirty seconds of human work — open the Blueprint, drop the input event node, and I can write everything hanging off it. That’s the shape of the collaboration right now: the agent writes the logic, and occasionally needs a hand placing a node it cannot conjure.
The level passed every test I could automate while being unplayable
I can’t send input through MCP, so mechanics get verified by moving the pawn and reading variables: teleport it onto a chip, ChipsHeld goes up and the chip is gone from the world; give it eleven chips, stand it in the door trigger, and the door reports IsOpen with its slide position climbing. That’s a real functional test of overlap → cast → variable → destroy, and it beats looking at a screenshot.
Every one of those passed on a level where the jump did nothing.
Two faults, both in properties that no gameplay test touches:
The controller owns the input, not the character. The GameMode paired the platforming character with the combat template’s PlayerController, and the controller is what registers the input mapping context. It registered the combat one, which maps neither jump nor dash. So the pawn had the jump event in its graph, JumpMaxCount at 2, a multi-jump handler, and no key routed to any of it.
AlwaysTickPose doesn’t refresh bones. The mannequin is hidden so only the robot’s pieces show. The skeletal mesh shipped set to evaluate its animation but skip bone-transform updates while invisible — which is a sensible optimisation and exactly wrong here, because the pieces are attached to those bones. The robot would have walked around frozen in the reference pose. AlwaysTickPoseAndRefreshBones is the one line that matters, and nothing about the symptom points at it.
Someone pressed A on a controller and both questions closed in about four seconds.
That’s the honest division of labour. Building: the agent, end to end, including the Blueprints. Verifying that state changes correctly: the agent, more reliably than I would by eye. Verifying that it plays: not without a person holding the controller. The gap isn’t the toolset being immature. It’s that reading properties is not the same category of knowledge as feeling a jump.
Measure it, don’t look at it
Five attempts went into getting the robot’s visor onto its face. It landed on the back of the head, then an ear, then the collarbone. Each attempt was: adjust a rotation, take a screenshot, squint.
Screenshots were the wrong instrument, in three separate ways that all look identical from outside. The game camera sits behind the character, so play-in-editor only ever shows me its back. An actor already placed in the level keeps its own component overrides and ignores changes to the Blueprint, so half my captures were of a stale copy. And the capture tool that takes a camera pose renders the editor world, where the play session’s pawn doesn’t exist — that one cost an hour, because a capture with no character in it looks exactly like an invisible character.
What settled it in one run: have the Blueprint print its own axes. A DoOnce in Tick, GetForwardVector / GetRightVector / GetUpVector on the attached component, then read it back out of the editor log through the logs toolset. With the pawn facing +X, the head bone came back as X = up, Y = forward, Z = sideways. From there the relative rotation for a mesh authored Z-up and X-forward is arithmetic rather than opinion: pitch 0, yaw 90, roll 90. First try.
There is no tool in this plugin for “what is this component’s world transform.” There doesn’t need to be. The Blueprint can compute anything, PrintString gets it out, and the log is readable through the same MCP session. Anything measurable in-engine can be gotten at that way, and it converges immediately where screenshots converge slowly or not at all.
What I’m keeping
The prototype itself stops here. It was the instrument, and it measured what I pointed it at:
- An agent can author gameplay Blueprints as text, and that’s the difference between this session and the last one. Not spawning actors — logic.
- It can build and place a level from data, idempotently, which makes iteration cheap.
- It can verify its own state changes better than I can, and cannot verify feel at all.
- Its limits reach into the design. A missing node type changed what the attack is.
The next genre goes through the same pipeline, and the list above is what I’ll check it against.
Ruiciro Rivera — Senior AI engineer, AI enthusiast, and builder of worlds. By day I build production LLM systems; by night I ship my own products with Claude — and, occasionally, a video game. Find me on LinkedIn and GitHub.
← back