10 KiB
Unreal MCP — Worked Recipes
Complete build sequences from a plain-English brief to a delivered capture.
Written against the discovery contract, because the live tool surface is
project-dependent: each step names the capability to locate (via
list_toolsets / describe_toolset) and the exact values to feed it —
not hardcoded tool names, which drift while the plugin is experimental.
Recipe grammar
Every step = four parts:
INTENT what this step achieves
DISCOVER which toolset/tool capability to use (locate via describe_toolset)
VALUES the exact arguments/numbers (from scene-craft.md)
VERIFY the query or screenshot that proves it worked
Dispatch shape: call_tool with toolset_name, tool_name, and an
arguments object matching the described schema — result returns on the
same turn.
LIGHTING RULE for every recipe: set Mobility = Movable on every light you spawn (Lumen GI ignores Static/Stationary lights — the #1 "why is GI dead" cause).
Session preamble for every recipe (do once):
list_toolsets→ note the qualified names (e.g.editor_toolset.toolsets.scene.SceneTools,EditorToolset.EditorAppToolset).describe_toolseton each group you'll touch → cache schemas.- Query current level (
SceneTools.get_current_level) and inventory the environment:find_actorsfor DirectionalLight, SkyAtmosphere, SkyLight, ExponentialHeightFog, PostProcessVolume, VolumetricCloud. Configure existing environment actors; spawn only what's missing — template levels ship with most of them, and duplicates compound into whiteouts. - Read the existing sun's
intensity— it tells you the scene's exposure calibration (template worlds are often calibrated aroundintensity: 10, not physical lux; see pitfalls 12b before applying scene-craft absolute values). - Locate your verification path:
EditorAppToolset.CaptureViewportwith acaptureTransformis the virtual camera — no viewport piloting needed.
Save the level + dirty packages after every phase marked 💾. One tool call at a time throughout — no batching, ever.
Recipe A — Daylight exterior clearing (blocking-first exterior)
Brief: "a sunny clearing with some rocks and a path"
Phase 1 — environment shell
- INTENT sky + sun + atmosphere exist and track each other.
DISCOVER actor-spawn capability (spawn by class).
VALUES spawn
SkyAtmosphere,SkyLight(real-time capture),DirectionalLightat rotation (0, −55, 40) [roll, pitch, yaw], intensity 90,000 lux, temperature 5,800 K, "atmosphere sun light" on;ExponentialHeightFogdensity 0.008. VERIFY actor list shows all four; screenshot reads as daytime sky, not black (if black: exposure — see Phase 3).
Phase 2 — ground & blocking
- INTENT walkable ground plane.
DISCOVER spawn-from-asset capability.
VALUES
/Engine/BasicShapes/Plane.Planeat (0,0,0), scale (100,100,1) → 100×100 m ground. LabelGround. - INTENT rock cluster + path silhouette from primitives (placeholder for
real assets if the project has none).
VALUES 5–9
/Engine/BasicShapes/Cube.Cubeat scattered locations within ±2,000 cm of origin, non-uniform scales between (1.5,1.5,1) and (4,3,2), yaws randomized 0–360°, sunk 10–30 cm into the ground so nothing floats. A path: 6–10 flattened cubes scale ≈(1.2,0.8,0.05) snaking through. VERIFY screenshot at eye height (camera z≈165) along the path axis: rocks read as varied, nothing floats, scale sane against the 180 cm yardstick (place one 180 cm-tall cylinder temporarily as a human stand-in, delete after checking). 💾
Phase 3 — exposure & mood
- INTENT deterministic exposure. DISCOVER PostProcessVolume spawn + property-set capability. VALUES PPV unbound=true, metering Manual, EV100 = 14.5. VERIFY screenshot: bright but not blown; shadows readable.
Phase 4 — deliver
- INTENT hero still.
VALUES
HighResShot 3840x2160from a framed viewpoint (see Recipe C Phase 2 for framing rules). VERIFY file exists in Saved/Screenshots;vision_analyzeagainst brief; iterate lighting yaw/fog once if flat. 💾 Report actor labels + paths.
Recipe B — Moody practical-lit interior
Brief: "a dim cozy room at night, warm lamp, blue moonlight through window"
Phase 1 — room shell from primitives
- VALUES floor: Cube at (0,0,−10) scale (6,6,0.2) → 6×6 m room. Four walls: cubes scale (6,0.2,3) / (0.2,6,3) positioned at ±300 on the respective axis, z=140 (walls 280 cm tall, sitting on the floor plane; keep tops at z≈290). Ceiling: cube scale (6,6,0.2) at z≈290 — spawn it LAST so you can screenshot the interior while open-topped. One window: leave a gap in a wall by using two shorter wall segments with a 120×120 cm opening at sill height 90 cm. VERIFY top-down + interior screenshots; door/window heights sane.
Phase 2 — lighting (the point of this recipe)
- INTENT kill the sun; interior reads as night. VALUES if the template level has a DirectionalLight: intensity → 0.05 lux temperature 4,300 K, pitch −20°, yawed to rake through the window (this is the "moon"). SkyLight intensity scale down to ≈0.05–0.1.
- INTENT warm practical. VALUES PointLight at lamp position (e.g. corner table, z≈120): 800 lumens (or ≈64 candela), temperature 2,700 K, attenuation radius 600 cm, source radius 10 cm (softer shadows).
- INTENT cool window rim. VALUES SpotLight outside the window aimed through it: 2,000 lumens, 6,500–8,000 K if faking without moon; skip if the directional moon already rakes through visibly. Inner/outer cone 25°/50°.
- INTENT exposure for dim interior. VALUES PPV unbound, Manual, EV100 = 4.5; fog: ExponentialHeightFog density 0.015 + volumetric fog on; practical's volumetric scattering intensity 2–4 so the lamp glows. VERIFY screenshot from a corner at z≈160: warm pool around lamp, cool slash from window, deep-but-readable shadows. The warm/cool split IS the deliverable — iterate intensities (never move both at once) until it reads. 💾
Phase 3 — dress & deliver
- VALUES if Starter Content exists, swap primitives:
/Game/StarterContent/ Props/SM_TableRound,SM_Chair,SM_Lamp_Ceiling, materialsM_Wood_Pineon floor,M_Basic_Wallon walls. Otherwise assign MaterialInstances with warm-neutral base colors to primitives. VERIFY finalHighResShot 3840x2160, vision-check, 💾, report.
Recipe C — Golden-hour cinematic still (camera craft)
Brief: "make it golden hour and give me a cinematic shot of "
Phase 1 — relight for golden hour
- VALUES DirectionalLight: intensity 12,000 lux, temperature 3,200 K, pitch −8°, yaw set so the sun is 30–60° OFF the camera axis behind the subject (rim + long shadows — never light flat from the camera). Fog density 0.02 + volumetric fog, sun volumetric scattering 2–6. PPV EV100 = 11. VERIFY screenshot: long shadows, warm rim on subject edges.
Phase 2 — the camera
- INTENT a framed shot WITHOUT touching the user's viewport.
DISCOVER
EditorAppToolset.CaptureViewportwithcaptureTransform— a virtual camera; no CineCamera or viewport piloting needed for stills. VALUES position: subject-distance by lens-equivalent framing — for a prop/monument subject ~500–800 cm back, height 120–160 cm; rotation aimed so the subject sits on a thirds intersection, horizon in upper or lower third. Slight upward pitch (+2° to +5°) from below eye height reads heroic and guarantees sky/horizon in frame. For an actual CineCameraActor (user wants a camera in the level, DoF, or a Sequencer shot): spawn/Script/CinematicCamera.CineCameraActor, set focal/aperture/focus via ObjectTools on its CineCameraComponent, then capture withcaptureTransformmatching its transform. VERIFY capture at viewport res first; iterate framing cheaply, then take the final.
Phase 3 — deliver
- VALUES
HighResShot 3840x2160(or user's target res) through the piloted camera. For a sequence/turntable instead of a still: this needs a Level Sequence with a Camera Cut track + Movie Render Queue — treat as its own task; warn about first-render shader-compile stall. VERIFY read file,vision_analyze: rim light present? focus falloff on the right plane? horizon off-center? Iterate at most twice, then deliver MEDIA: path + what was changed. 💾
Recipe D — Import an asset and populate the scene
Brief: "here's model.fbx / a Fab asset — put a ring of them around the fountain"
Phase 1 — import
- INTENT asset lands in
/Game/Importedwith no dialog stall. DISCOVER import capability (if none advertised: custom toolset wrappingunreal.AssetImportTaskwithautomated=True— see tool-surface.md; theautomatedflag is what prevents a modal import dialog from freezing the whole MCP loop). VALUES destination/Game/Imported, save=true. VERIFY asset-exists query on the resulting long package name; spawn one instance at origin, screenshot, check scale against 180 cm yardstick — DCC exports are routinely 100× off (meters vs centimeters). Fix by actor scale or reimport with unit conversion.
Phase 2 — populate
- INTENT ring of N instances around a center C. VALUES for i in 0..N−1: angle θ=360·i/N, position = C + (r·cosθ, r·sinθ, 0) with r = fountain radius + clearance (e.g. 350 cm), yaw = θ+90° so each faces the center (or +270° to face outward — check one instance first and LOOK). Spawn one, verify facing, then loop the rest one call at a time. VERIFY count query matches N; screenshot from above (camera z≈1,500 looking down) for spacing; eye-level screenshot for scale. 💾
Phase 3 — deliver
- Report: asset path, N instances with label prefix, level saved, overview + eye-level captures as MEDIA paths.
When a recipe's capability is missing
If discovery shows no shipped tool for a step (no import tool, no console-exec for HighResShot, etc.):
- Say so plainly; don't fake the step.
- Offer the custom-toolset path (tool-surface.md) — a 20-line Python
toolset usually covers the gap; it needs
RefreshTools+ session restart to appear. - Or hand the user the one-liner to run in the editor's Python/console themselves, with exact text.
Never claim a phase done without its VERIFY evidence. The user can't check the editor for you — the screenshots are the ground truth they see.