- 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
164 lines
8.2 KiB
Markdown
164 lines
8.2 KiB
Markdown
# 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](m0-spike-1000x640.png)._
|
||
|
||
## What M0 delivers
|
||
|
||
- `bevy_forge_runtime` — a new crate in the workspace, added to a game with
|
||
`.add_plugins(EditorPlugin)` after `DefaultPlugins`.
|
||
- Fixed editor shell: toolbar, hierarchy (left), inspector (right), status bar; F1 or the toolbar
|
||
button toggles the whole editor.
|
||
- Live hierarchy: `Children`/`ChildOf` tree, indentation, scrolling, click selection, selected-row
|
||
highlight.
|
||
- Reflection inspector for `Name`, `Transform` and `Visibility`; translation X/Y/Z are editable via
|
||
`bevy_ui_widgets` sliders 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
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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_runtime` passes; `cargo test -p bevy_forge_runtime` is **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 egui` finds no egui; a plain tree grep for `egui` is empty.
|
||
The v1 editor still uses egui; the runtime crate does not. `bevy_ui_widgets` resolves to 0.20.0.
|
||
- The example launches, logs `editor shell ready`, `hierarchy: 10 rows, 7 roots`,
|
||
`inspector: selected ... (Hero Cube)` and `gizmo: 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`, `Ground` and `Hero Cube` in 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_core` is 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_rows` remains the literal tree
|
||
model for tests.
|
||
- The inspector covers `Name`/`Transform`/`Visibility`; generic `TypeRegistry` component 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.
|