- new bevy_forge_runtime crate with EditorPlugin, EditorState, Selection - native Bevy UI shell + official bevy_ui_widgets 0.20 (no egui in runtime) - live hierarchy, reflection inspector, translate gizmo, editor camera - deterministic 1440x900/1000x640 screenshot mode - demo example, docs and screenshots; v1 crates untouched - interactive computer-use test fixed inspector command-queue panic
8.2 KiB
Bevy Forge v2 — M0 Spike
Status: M0 spike, not a release. It proves the v2 architecture: a real in-game Bevy 0.20 editor plugin with native Bevy UI, a live hierarchy, a reflection inspector, one translate gizmo and deterministic screenshots. No egui is involved in the runtime crate.
1000×640 variant: m0-spike-1000x640.png.
What M0 delivers
bevy_forge_runtime— a new crate in the workspace, added to a game with.add_plugins(EditorPlugin)afterDefaultPlugins.- Fixed editor shell: toolbar, hierarchy (left), inspector (right), status bar; F1 or the toolbar button toggles the whole editor.
- Live hierarchy:
Children/ChildOftree, indentation, scrolling, click selection, selected-row highlight. - Reflection inspector for
Name,TransformandVisibility; translation X/Y/Z are editable viabevy_ui_widgetssliders and update the ECS entity. Rotation/scale/visibility are read-only in M0. - Editor camera (right-drag orbit, middle-drag pan, wheel zoom) and a translate gizmo with screen-space axis picking and ray/plane dragging.
- Deterministic screenshot mode at fixed sizes with a scale factor override.
- A small hardcoded 3D demo scene (nine named scene entities plus the editor camera) in
crates/bevy_forge_runtime/examples/editor_spike.rs.
Requirements
- Rust 1.85+ (the workspace pins
rust-version = "1.85"; this environment used 1.99). - Linux with Vulkan/GL and a windowing session (Wayland or X11). The reference run is Wayland with an NVIDIA RTX 3050; the UI and screenshots also work through the probe on this machine.
- No Bevy installation is required; Bevy 0.20.0 is a normal Cargo dependency.
Run
cd bevy-forge
cargo run -p bevy_forge_runtime --example editor_spike
The first run compiles the Bevy UI/PBR pipeline and is slow. The window opens at 1440×900.
Controls
| Input | Action |
|---|---|
| F1 / toolbar Editor (F1) | Show or hide the editor UI |
| Left click a hierarchy row | Select that entity |
| Left drag a gizmo axis | Translate the selected entity along X/Y/Z |
| Right drag in the viewport | Orbit the editor camera |
| Middle drag in the viewport | Pan the editor camera |
| Mouse wheel | Zoom the editor camera |
The inspector sliders edit Transform.translation.x/y/z in the range -20..=20 with 0.1 steps.
A gizmo drag updates the ECS Transform, and the inspector sliders follow.
Deterministic screenshots
The screenshot mode is opt-in and exits after saving:
FORGE_SCREENSHOT=$PWD/.swarm/m0/evidence/m0-1440x900.png \
FORGE_SCREENSHOT_SIZE=1440x900 \
cargo run -p bevy_forge_runtime --example editor_spike
FORGE_SCREENSHOT=$PWD/.swarm/m0/evidence/m0-1000x640.png \
FORGE_SCREENSHOT_SIZE=1000x640 \
cargo run -p bevy_forge_runtime --example editor_spike
| Variable | Default | Meaning |
|---|---|---|
FORGE_SCREENSHOT |
unset | output PNG; unset disables the mode |
FORGE_SCREENSHOT_SIZE |
1440x900 |
WIDTHxHEIGHT, 320..=7680 |
FORGE_SCREENSHOT_FRAMES |
300 |
minimum frames before capture |
FORGE_SCREENSHOT_WARMUP_SECS |
10 |
minimum wall-clock seconds before capture |
The warmup is deliberate: UI/gizmo shaders are ready quickly, but the 3D mesh/PBR pipeline can take
~10 s to compile on a cold run. A 30-frame screenshot is blank; the mode waits for both thresholds.
The window is forced to WindowResolution::new(w, h).with_scale_factor_override(1.0), so the PNG is
exactly the requested size on HiDPI displays.
Determinism caveat: the hierarchy hover highlight follows the pointer. Park the cursor outside the editor window before capturing if you need byte-identical pixels; the committed evidence was captured that way. The screenshot mode fixes size, scene and frame timing, but does not currently neutralise pointer hover.
Architecture
crates/bevy_forge_runtime/
├── src/lib.rs public prelude: EditorPlugin, EditorState, Selection, InspectorSource
├── src/plugin.rs resources, plugin registration, selection cleanup
├── src/ui/ fixed shell, theme, markers, EditorLayout
├── src/hierarchy.rs live ListBox/ScrollArea tree + selection observer
├── src/inspector.rs reflection summary/apply, transform sliders, live sync
├── src/gizmo.rs editor camera, axes drawing, picking/drag
├── src/screenshot.rs deterministic PNG mode
└── examples/editor_spike.rs
The editor UI is an overlay: the 3D camera renders full-window and the panels cover the left/right
edges. EditorLayout::viewport_rect defines the center rectangle used by gizmo input.
M0 acceptance
The full evidence is in .swarm/m0/reviews/V01.md and .swarm/m0/final-report.md; the commands
are listed in .swarm/m0/plan.md. In short:
cargo check -p bevy_forge_runtimepasses;cargo test -p bevy_forge_runtimeis 38 passed, 0 failed, 0 ignored plus one ignored doctest; the full workspace is 384 passed, 0 failed, 2 ignored (v1 + M0).cargo tree -p bevy_forge_runtime -i eguifinds no egui; a plain tree grep foreguiis empty. The v1 editor still uses egui; the runtime crate does not.bevy_ui_widgetsresolves to 0.20.0.- The example launches, logs
editor shell ready,hierarchy: 10 rows, 7 roots,inspector: selected ... (Hero Cube)andgizmo: camera ready, with no warnings/panics. - The gizmo is visible on the selected entity; its drag math is unit-tested (including a rotated parent). Computer-use on 2026-10-27 clicked hierarchy rows, dragged the inspector X slider and the X gizmo axis, toggled the UI and zoomed the camera; selection, inspector, ECS and gizmo followed, with no panic.
- Screenshots are exactly 1440×900 and 1000×640; both show the shell, hierarchy, inspector and gizmo.
- No v1/core/CLI source changes; only the workspace member/lock, the new crate, docs and README.
- Independent review: pass_with_risks (
.swarm/m0/reviews/V01.md, local coordination artifact). All acceptance gates pass; the residual risks are right/middle orbit-pan (not scripted) and the disclosed hierarchy root filter.
Interactive verification (computer-use, 2026-10-27)
The editor was driven through the KDE Wayland remote-desktop portal with the computer-use tool:
- clicked
Red Cube,Blue Cube,GroundandHero Cubein the hierarchy — selection, inspector fields and gizmo moved to the clicked entity every time; - dragged the Red Cube Translation X slider from −2.6 to 3.2 — the cube and gizmo moved and the inspector value updated;
- dragged the Hero Cube X gizmo axis from 0.0 to 1.0 — the cube moved and the inspector slider followed;
- the toolbar button hid the UI; F1 restored it; wheel zoom changed the camera distance.
This found a real bug that unit tests missed: on a selection change, the inspector's transform-sync
system ran after the panel rebuild and queued a SliderValue insert for a slider that had just been
despawned, panicking in Bevy's command queue. The systems are now chained sync-first, then
rebuild; repeated selection/slider/gizmo runs are clean. Orbit and pan (right/middle drag) are not
scripted by the portal drag helper and remain a manual check; wheel zoom is covered.
Limits (M0, by design)
- No save/load of
.forge.ron, no BSN export from the UI, no undo/redo, no play/pause, no asset browser, no docking, no multi-select, no rotate/scale gizmo. - The demo scene is hardcoded;
bevy_forge_coreis not a runtime dependency yet. - The live hierarchy panel lists named, transformed scene roots and their descendants; unnamed root
entities are omitted in M0 (a full-world mode is M1 work).
collect_rowsremains the literal tree model for tests. - The inspector covers
Name/Transform/Visibility; genericTypeRegistrycomponent discovery is M1 work. - Parent scale is ignored during gizmo drags (M0); rotations are honored.
- 3D only in the demo; the v2 plan adds 2D and scene loading in M1/M2.
Next
M1 Core: .forge.ron save/load via bevy_forge_core, selection API/multi-select, undo/redo command
stack, editor camera polish, and the first integration tests against the real scene format. Do not
start M1 until M0 is accepted by the verifier and the actual request/token spend is recorded.
