Files
unreal-engine-mcp-system-pl…/UI_WORKFLOW.md
Bonchellon eba71c4ca8 Unified, portable Unreal Engine MCP system plugin
Merge of the two project copies into one self-contained plugin (the superset:
variable_op + variables.py, full pcg_op runtime/declarative/preset ops, the
CreateWidgetFloatAnimation widget tool, and full Voxel graph authoring).

Made fully project- and machine-agnostic — no hardcoded paths:
- New src/projectPaths.js auto-detects the host .uproject (walk-up), project
  name, Editor build target, log file, and engine install (EngineAssociation
  via launcher manifest/registry, else installed-engine scan). All overridable
  via UE_* env vars.
- Rewired buildOrchestrator/insights/launcher/insightsExporter/server.js and the
  Python workers (cpp_scaffold, live_coding, apply_graph, console) off the old
  C:/Github/ihy, IHY*, E:/UE_Versions and UE_5.7 literals onto the resolver.
- Voxel made optional: Build.cs auto-detects the Voxel plugin (env
  UE_MCP_WITH_VOXEL override) and the C++ compiles to stubs under WITH_MCP_VOXEL,
  so the module builds in projects without Voxel; .uplugin marks Voxel optional.
- De-branded the agent-gateway and docs; scrubbed a leaked API key; excluded
  node_modules/Binaries/Intermediate/__pycache__/secrets from the repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 01:07:24 +03:00

274 lines
12 KiB
Markdown

# UI_WORKFLOW — UMG/Slate UI authoring guide for AI agents
> **READ THIS BEFORE EDITING ANY `WBP_*` / UMG / SLATE / UI.**
> If you start a UI task without following this file, you will produce a generic
> placeholder ("prototype look"). This document is the contract that turns
> "ugly UMG demo" into "shippable UI".
## 0. Quick decision flow
```
user asks for UI work
├─ Is there a DA_UITheme asset in /Game/UI/Theme/ ? ── no ─→ STEP A. Build theme first.
│ (one-time setup, ~5 min)
├─ Are there base components in /Game/UI/Components/ ? ── no ─→ STEP B. Build component library.
├─ Did the user give a reference image / mood / brand? ── no ─→ STEP C. Ask for one before coding.
└─ All three present? ─→ STEP D. Compose screen → screenshot → iterate.
```
You **must not** invent palettes, spacing, or component shapes on the fly. If
a token is missing, you ADD it to the theme. If a component is missing, you
CREATE it in the library. The screen WBP only composes.
---
## STEP A — The Theme (one DataAsset to rule them all)
### A.1 Class
Create C++ DataAsset `UProjectUITheme : UPrimaryDataAsset` (use `cpp_scaffold_op`):
```cpp
USTRUCT(BlueprintType) struct FProjectUIPalette {
GENERATED_BODY()
UPROPERTY(EditAnywhere) FLinearColor Primary = FLinearColor::White;
UPROPERTY(EditAnywhere) FLinearColor Accent = FLinearColor(1, 0.5, 0.1, 1);
UPROPERTY(EditAnywhere) FLinearColor Danger = FLinearColor::Red;
UPROPERTY(EditAnywhere) FLinearColor Surface = FLinearColor(0.05, 0.05, 0.05, 1);
UPROPERTY(EditAnywhere) FLinearColor SurfaceAlt = FLinearColor(0.1, 0.1, 0.1, 1);
UPROPERTY(EditAnywhere) FLinearColor Ink = FLinearColor(0.9, 0.9, 0.85, 1);
UPROPERTY(EditAnywhere) FLinearColor Muted = FLinearColor(0.5, 0.5, 0.45, 1);
};
USTRUCT(BlueprintType) struct FProjectUIType {
GENERATED_BODY()
UPROPERTY(EditAnywhere) FSlateFontInfo H1; // 48pt display
UPROPERTY(EditAnywhere) FSlateFontInfo H2; // 32pt headings
UPROPERTY(EditAnywhere) FSlateFontInfo Body; // 16pt text
UPROPERTY(EditAnywhere) FSlateFontInfo Caption;// 12pt small
UPROPERTY(EditAnywhere) FSlateFontInfo Mono; // monospace / terminal text
};
UCLASS()
class PROJECTUI_API UProjectUITheme : public UPrimaryDataAsset {
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category="Theme") FProjectUIPalette Palette;
UPROPERTY(EditAnywhere, Category="Theme") FProjectUIType Type;
// Spacing rhythm — use index, not raw px.
UPROPERTY(EditAnywhere, Category="Theme") TArray<float> Spacing = {4, 8, 12, 16, 24, 32, 48, 64};
UPROPERTY(EditAnywhere, Category="Theme") float CornerRadius = 4.f;
UPROPERTY(EditAnywhere, Category="Theme") float BorderWeight = 1.f;
// Named brushes — referenced by their FName key from WBPs.
UPROPERTY(EditAnywhere, Category="Theme") TMap<FName, FSlateBrush> Brushes;
};
```
### A.2 Instance
Create instance at `/Game/UI/Theme/DA_UITheme` (one per visual mode — fork as
`DA_UITheme_<Mode>` if a project needs several). Example tuning:
| Token | Value | Why |
|---|---|---|
| Palette.Surface | `(0.04, 0.05, 0.05, 1)` | near-black base |
| Palette.SurfaceAlt | `(0.09, 0.1, 0.09, 1)` | row striping |
| Palette.Ink | `(0.87, 0.91, 0.83, 1)` | off-white, never pure |
| Palette.Accent | `(0.95, 0.62, 0.21, 1)` | amber highlight |
| Palette.Danger | `(0.78, 0.10, 0.10, 1)` | muted red, not hex red |
| Palette.Muted | `(0.45, 0.50, 0.45, 1)` | green-grey |
| Type.H1 | font `Roboto Mono Bold`, 48, letter spacing +1 | display |
| Type.Body | font `Inter Regular`, 16 | readable body |
| CornerRadius | 0 | sharp / brutalist |
| BorderWeight | 1.5 | thicker → more readable |
### A.3 Access
Every WBP holds `UPROPERTY(EditDefaultsOnly) UProjectUITheme* Theme;` set in Class
Defaults to `DA_UITheme`. No hardcoded colors anywhere. If you catch yourself
typing `FLinearColor(0.7, 0.1, 0.1, 1)` — stop, add a token to the theme instead.
---
## STEP B — Component Library
Build these once, reuse forever. All under `/Game/UI/Components/`.
### Required base set
| Component | Purpose | Variants |
|---|---|---|
| `WBP_Button` | All clickable | Primary, Ghost, Danger, Icon-only |
| `WBP_Card` | Container w/ header/body/footer | Flat, Raised, Outlined |
| `WBP_HUDPanel` | Framed panel (corner brackets, overlay channel) | Top, Bottom, Floating |
| `WBP_StatBar` | Numeric stat bar (health/energy/progress) | Horizontal, Vertical, Circular |
| `WBP_Label` | Text wrapper that auto-picks Type token | H1, H2, Body, Caption, Mono |
| `WBP_Divider` | 1px line | Solid, Dashed, Glitchy |
| `WBP_Icon` | Wraps `Image` with theme tint | size=spacing token |
| `WBP_ListRow` | One row of a list, themed | Selected/Hovered states wired |
| `WBP_Modal` | Centered overlay with backdrop | Confirm, Form, Info |
| `WBP_Toast` | Bottom-slide notification | Info, Warn, Error |
### Authoring rules
- **No hardcoded sizes** — use `Theme->Spacing[idx]` (e.g. padding = `Spacing[3]` = 16px).
- **Every interactive widget has 4 states wired**: Normal, Hovered, Pressed, Disabled. Hooked from theme brushes, not duplicated.
- **Each component exposes `SetVariant(EVariant)` and `SetState(EState)` BP-callable funcs** — so the screen WBP composes by setting flags, not by hand-tweaking child widgets.
- **Animation:** every state transition has a 80-150ms ease. Define one `WidgetAnimation` per state in `WBP_Button`, copy pattern to others. No animation = feels dead.
### Optional stylized overlays
- `WBP_HUDPanel` can expose a Niagara/Material overlay channel — bind project-specific material instances (e.g. `MI_UI_Scanline`, `MI_UI_Noise`) as defaults if your project has them.
- `WBP_Label` Mono variant can use a chromatic-aberration material instance.
- Background of every HUD piece: prefer a themed `MI_UI_Surface` material over a flat solid color when a stylized look is wanted.
---
## STEP C — Reference parsing protocol
When user supplies a reference image, **do not** start coding. First emit:
```
REFERENCE TOKENS
================
Palette (6 colors HEX, ordered by dominance):
1. Primary surface: #__
2. Surface alt: #__
3. Ink (text): #__
4. Accent: #__
5. Danger/warning: #__
6. Muted/secondary: #__
Typography:
H1 size: __pt weight: __ tracking: __
Body size:__pt weight: __
Family hint: __ (serif/sans/mono/condensed)
Spacing rhythm (3 visible gaps in px): __ __ __
→ suggested Spacing array: [__, __, __, __, __, __, __, __]
Corner radius observed: __px (0 = sharp / brutalist)
Border weight observed: __px
Visual hierarchy (one sentence): __
Mood (3 adjectives): __, __, __
Departures from current DA_UITheme:
- Theme.Palette.__ should become #__ (was #__)
- Theme.Spacing[__] should become __ (was __)
```
Then ask the user: "apply to existing theme or fork as `DA_UITheme_<NewName>`?"
Once approved, use `audio_op set_property` / direct asset edits to mutate the
theme DataAsset. Never edit individual WBPs to match a ref — change the theme,
all WBPs follow.
---
## STEP D — The Vision Loop (most important)
This is what makes AI UI not suck. After ANY structural change, you MUST:
```
1. widget_op apply_tree / set_property / ... (your edit)
2. widget_op compile_save {bp_path}
3. widget_op screenshot {bp_path, size: [1920, 1080]}
4. Read the returned png_path. If you are a vision-capable model:
open the file, look at the result, list 3 specific problems before next edit.
5. Loop until no problems remain.
```
### Hard rule
- **Maximum 2 blind edits** (without screenshot) per session. After that, MUST screenshot. If a user reports "it looks bad", your first action is `widget_op screenshot`, not asking for clarification.
- **Before declaring "done"**, last action MUST be a screenshot, and you MUST describe what you see (not what you intended).
- The screenshot landing path is `<Project>/Saved/MCP/widgets/<bp_name>.png`. Always attach this to your turn output.
### What to look for in the screenshot
- **Alignment grid** — are baselines and gutters consistent? Use a mental 8px grid.
- **Hierarchy** — can you ID primary action in <1s by squinting?
- **Density** is it crammed (Unity-tutorial vibe) or breathing?
- **Color count** is anything outside the theme palette? If yes, that's a bug.
- **Edge cases** empty list, longest possible label (16 chars), worst-case localization (German is +30%).
---
## STEP E — Screen composition
When user asks for a new screen (`WBP_MainMenu`, `WBP_HUD_InGame`, ...):
1. Sketch hierarchy in text first:
```
WBP_MainMenu (RootCanvas)
WBP_HUDPanel "title-bar" (anchored top, span)
WBP_Label H1 "GAME TITLE"
WBP_Card "menu-list" (centered, 480x600)
WBP_Button Primary "Continue"
WBP_Button Ghost "New Game"
WBP_Button Ghost "Settings"
WBP_Button Danger "Quit"
WBP_Label Caption "v0.4.2 • Build 2026-05-28" (bottom-left)
```
2. Confirm with user (one screen = one confirmation step, not per widget).
3. Build with `widget_op apply_tree` (single transaction, single undo).
4. Compile + screenshot.
5. Iterate per Step D loop.
---
## Naming & paths (enforced)
| Asset | Path | Naming |
|---|---|---|
| Theme DataAsset | `/Game/UI/Theme/` | `DA_UITheme_*` |
| Base components | `/Game/UI/Components/` | `WBP_<Name>` |
| Screens | `/Game/UI/Screens/` | `WBP_<Screen>` (no prefix like "Menu_" — flat) |
| Materials | `/Game/UI/Materials/` | `M_UI_*`, `MI_UI_*` |
| Icons | `/Game/UI/Icons/` | `T_UI_Icon_<verb>_<noun>` |
| Frame brushes | `/Game/UI/Frames/` | `T_UI_Frame_*` |
If asked to create UI assets outside these paths, ask why. Probably a mistake.
---
## Things NEVER to do (anti-patterns AI agents fall into)
1. ❌ Hardcoded color `FLinearColor(0.2, 0.2, 0.2)` anywhere. → Use theme.
2. ❌ Hardcoded padding `Padding=8`. → Use `Theme->Spacing[1]`.
3. ❌ One giant CanvasPanel screen with absolute coords for everything. → Use HorizontalBox/VerticalBox/Grid composition.
4. ❌ "Just add a `Border` widget" for a card. → Use `WBP_Card` component.
5. ❌ Per-screen unique button styling. → Add a variant to `WBP_Button`.
6. ❌ Default UE font (Roboto). → Always pick from `Theme->Type`.
7. ❌ Default `Button` widget visible to the user. → Always wrap in `WBP_Button` because state animations live there.
8. ❌ Skip Step D. → If you didn't screenshot, you didn't finish.
9. ❌ Generate icons inline as text "≡" or "✕". → Use `WBP_Icon` with a real Texture2D.
---
## Required first action for any UI session
```
discover_op find_bp_by_parent { parent_class_path: "/Script/ProjectUI.ProjectUITheme" }
```
- 0 hits → STEP A (build theme).
- 1+ hits → fetch the `DA_UITheme` palette via `validation_op get_references` or read directly, cache token names in your working memory.
```
discover_op find_bp_by_parent { parent_class_path: "/Script/UMG.UserWidget" }
```
List existing components. If `/Game/UI/Components/WBP_Button` missing → STEP B.
Then and only then proceed to the user's actual request.
---
## Why this works
Without this guide, AI generates UMG by sampling "what UMG typically looks like
on GitHub" — boring, generic, grey. With this guide:
- **Decisions are constrained** (theme tokens, component variants) → no more "AI inventing aesthetics from scratch every time".
- **Composition replaces design** → LLMs are great at composing; bad at painting.
- **Vision loop closes the feedback gap** → AI sees its output, judges it, fixes it.
- **References parse to data**, not vibes → reproducible, debuggable, diffable.
Result: shippable UI in 1-2 iterations instead of 15.