Skip to content

Add standalone bevy_entity_inspector and bevy_transform widget for drawing on the UI #215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
53c89d2
Add Component Viewer pane and integrate bevy_remote functionality by …
jbuehler23 Jun 20, 2025
c729f1c
Refactor connect_to_remote function to use IoTaskPool for asynchronou…
jbuehler23 Jun 23, 2025
a97f4e4
Add JSON deserialization for the bevy_remote responses, as well as im…
jbuehler23 Jun 24, 2025
b4b6937
Code cleanup
jbuehler23 Jun 24, 2025
2a8eb81
Refactor changes to create a new bevy_transform widget, and bevy_enti…
jbuehler23 Jun 26, 2025
3c13346
Major refactor to use rows of entity data, and reflected components t…
jbuehler23 Jul 4, 2025
5903ae2
feat: Refactor Bevy Entity Inspector with Event-Driven Architecture a…
jbuehler23 Jul 9, 2025
e59e0dd
chore: Remove outdated CHANGELOG and PR_DESCRIPTION files after refac…
jbuehler23 Jul 9, 2025
f0fe730
docs: add more docs to fix warnings
jbuehler23 Jul 9, 2025
ab58b16
feat: Add entity inspector with property panel and event handling
jbuehler23 Jul 9, 2025
9240695
refactor: Update UI layout and improve property panel width for bette…
jbuehler23 Jul 9, 2025
b0a1256
Merge branch 'main' of https://github.com/bevyengine/bevy_editor_prot…
jbuehler23 Jul 9, 2025
cdca15d
feat: Add missing dependency for bevy_remote in Cargo.toml
jbuehler23 Jul 9, 2025
51feed0
Update tree.rs
jbuehler23 Jul 10, 2025
dcd5dfb
refactor: Remove bevy_transform plugin and associated files
jbuehler23 Jul 10, 2025
65fbf09
Merge branch 'investigate-brp-usage' of https://github.com/bevyengine…
jbuehler23 Jul 10, 2025
4fc287e
docs: Enhance documentation across entity inspector modules with rela…
jbuehler23 Jul 10, 2025
0092e30
docs: Improve documentation for tree structure components and their i…
jbuehler23 Jul 10, 2025
ecbc03c
Merge branch 'main' into investigate-brp-usage
jbuehler23 Jul 10, 2025
bb26983
refactor: Reorder imports in cube_server.rs for clarity
jbuehler23 Jul 10, 2025
bddf2e4
Merge branch 'investigate-brp-usage' of https://github.com/bevyengine…
jbuehler23 Jul 10, 2025
1c8e4f5
fix clippy!
jbuehler23 Jul 10, 2025
df9b950
refactor: Change ui module from public to private
jbuehler23 Jul 10, 2025
87fb203
docs: Fix formatting in inspector example documentation
jbuehler23 Jul 10, 2025
3181962
fix: Correct documentation formatting and improve clarity in reflecti…
jbuehler23 Jul 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ bevy = { git = "https://github.com/bevyengine/bevy.git", rev = "a3d406dd49720525
] }
bevy_derive = { git = "https://github.com/bevyengine/bevy.git", rev = "a3d406dd497205253e34ace757ab0076d50eec14" }
bevy_macro_utils = { git = "https://github.com/bevyengine/bevy.git", rev = "a3d406dd497205253e34ace757ab0076d50eec14" }
bevy_remote = { git = "https://github.com/bevyengine/bevy.git", rev = "a3d406dd497205253e34ace757ab0076d50eec14" }

thiserror = "2.0"
serde = { version = "1", features = ["derive"] }
serde_json = "1.0.140"
ureq = {version = "3.0.12", features = ["json"]}
tracing-test = "0.2.5"
tracing = "0.1.41"
atomicow = "1.1.0"
Expand Down Expand Up @@ -64,6 +68,8 @@ bevy_toolbar = { path = "bevy_widgets/bevy_toolbar" }
bevy_tooltips = { path = "bevy_widgets/bevy_tooltips" }
bevy_text_editing = { path = "bevy_widgets/bevy_text_editing" }
bevy_field_forms = { path = "bevy_widgets/bevy_field_forms" }
bevy_focus = { path = "bevy_widgets/bevy_focus" }
bevy_transform = { path = "bevy_widgets/bevy_transform" }

# general crates
bevy_editor_core = { path = "crates/bevy_editor_core" }
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_editor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ bevy_menu_bar.workspace = true
bevy_footer_bar.workspace = true
bevy_context_menu.workspace = true
bevy_editor_styles.workspace = true
bevy_remote.workspace = true

serde.workspace = true
ron.workspace = true
Expand Down
110 changes: 94 additions & 16 deletions crates/bevy_editor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
//! which transforms the user's application into an editor that runs their game.
//! - Finally, it will be a standalone application that communicates with a running Bevy game via the Bevy Remote Protocol.

use std::env;

use bevy::app::App as BevyApp;
use bevy::color::palettes::tailwind;
use bevy::math::ops::cos;
use bevy::prelude::*;
// Re-export Bevy for project use
pub use bevy;
Expand All @@ -24,6 +28,8 @@ use bevy_editor_styles::StylesPlugin;
use bevy_2d_viewport::Viewport2dPanePlugin;
use bevy_3d_viewport::Viewport3dPanePlugin;
use bevy_asset_browser::AssetBrowserPanePlugin;
use bevy_remote::http::RemoteHttpPlugin;
use bevy_remote::RemotePlugin;

use crate::load_gltf::LoadGltfPlugin;

Expand All @@ -48,18 +54,25 @@ impl Plugin for EditorPlugin {
// Update/register this project to the editor project list
project::update_project_info();
info!("Loading Bevy Editor");

bevy_app
.add_plugins((
RemotePlugin::default(),
RemoteHttpPlugin::default(),
EditorCorePlugin,
ContextMenuPlugin,
StylesPlugin,
Viewport2dPanePlugin,
Viewport3dPanePlugin,
ui::EditorUIPlugin,
AssetBrowserPanePlugin,
LoadGltfPlugin,
AssetBrowserPanePlugin,
))
.add_systems(Startup, dummy_setup);
.add_systems(Startup, setup)
.add_systems(Update, move_cube)
.register_type::<Cube>()
.register_type::<MyObject>()
.register_type::<MoveSpeed>();
}
}

Expand All @@ -76,7 +89,7 @@ impl App {

/// Run the application
pub fn run(&self) -> AppExit {
let args = std::env::args().collect::<Vec<String>>();
let args = env::args().collect::<Vec<String>>();
let editor_mode = !args.iter().any(|arg| arg == "-game");

let mut bevy_app = BevyApp::new();
Expand All @@ -85,35 +98,100 @@ impl App {
bevy_app.add_plugins(EditorPlugin);
}

info!(
"Running bevy editor in {} mode",
if editor_mode { "editor" } else { "game" }
);
bevy_app.run()
}
}

/// This is temporary, until we can load maps from the asset browser
fn dummy_setup(
#[derive(Component, Reflect)]
#[reflect(Component)]
struct Cube(f32);

#[derive(Component, Reflect)]
#[reflect(Component)]
struct MyObject {
vec3: Vec3,
color: Color,
}

#[derive(Resource, Reflect)]
#[reflect(Resource)]
struct MoveSpeed {
value: f32,
}

fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials_2d: ResMut<Assets<ColorMaterial>>,
mut materials_3d: ResMut<Assets<StandardMaterial>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// unnamed object
commands.spawn((
Name::new("MyObject1"),
MyObject {
vec3: Vec3::new(1.0, 2.0, 3.0),
color: Color::from(tailwind::BLUE_500),
},
));

// cube
let cube_handle = meshes.add(Cuboid::new(1.0, 1.0, 1.0));
commands.spawn((
Mesh2d(meshes.add(Circle::new(50.0))),
MeshMaterial2d(materials_2d.add(Color::WHITE)),
Name::new("Circle"),
Name::new("Cube"),
Mesh3d(cube_handle.clone()),
MeshMaterial3d(materials.add(Color::from(tailwind::RED_200))),
Transform::from_xyz(0.0, 0.5, 0.0),
Cube(1.0),
children![(
Name::new("Sub-cube"),
Mesh3d(cube_handle.clone()),
MeshMaterial3d(materials.add(Color::from(tailwind::GREEN_500))),
Transform::from_xyz(0.0, 1.5, 0.0),
children![(
Name::new("Sub-sub-cube"),
Mesh3d(cube_handle),
MeshMaterial3d(materials.add(Color::from(tailwind::BLUE_800))),
Transform::from_xyz(1.5, 0.0, 0.0),
)]
)],
));

// circular base
commands.spawn((
Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.5)))),
MeshMaterial3d(materials_3d.add(Color::WHITE)),
Name::new("Plane"),
Name::new("Circular base"),
Mesh3d(meshes.add(Circle::new(4.0))),
MeshMaterial3d(materials.add(Color::from(tailwind::GREEN_300))),
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
));

// light
commands.spawn((
DirectionalLight {
Name::new("Light"),
PointLight {
shadows_enabled: true,
..default()
},
Transform::default().looking_to(Vec3::NEG_ONE, Vec3::Y),
Name::new("DirectionalLight"),
Transform::from_xyz(4.0, 8.0, 4.0),
));

// camera
commands.spawn((
Name::new("Camera"),
Camera3d::default(),
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::Y, Vec3::Y),
));
}

fn move_cube(
mut query: Query<&mut Transform, With<Cube>>,
time: Res<Time>,
move_speed_res: Option<Res<MoveSpeed>>,
) {
let move_speed = move_speed_res.map(|res| res.value).unwrap_or(1.0);
for mut transform in &mut query {
transform.translation.y = -cos(time.elapsed_secs() * move_speed) + 1.5;
}
}
2 changes: 1 addition & 1 deletion crates/bevy_editor_cam/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ bevy.workspace = true
bevy_derive.workspace = true

[dev-dependencies]
bevy = { workspace = true, features = ["jpeg", "ktx2", "zstd"] }
bevy = { workspace = true, features = ["jpeg", "ktx2"] }
rand = "0.8"

[lints]
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_editor_launcher/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ bevy_editor.workspace = true
bevy_scroll_box.workspace = true
bevy_footer_bar.workspace = true
bevy_editor_styles.workspace = true
bevy_remote.workspace = true

rfd.workspace = true
serde.workspace = true
ron.workspace = true
serde_json.workspace = true
30 changes: 30 additions & 0 deletions crates/bevy_entity_inspector/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "bevy_entity_inspector"
version = "0.1.0"
edition = "2021"
description = "A modular entity inspector for Bevy with reflection support"
authors = ["Joe Buehler"]
license = "MIT OR Apache-2.0"
keywords = ["bevy", "inspector", "editor", "reflection", "ui", "debug"]
categories = ["game-development", "gui"]

[features]
default = ["remote"]
remote = ["dep:bevy_remote", "dep:serde", "dep:serde_json", "dep:ureq"]

[dependencies]
bevy = { git = "https://github.com/bevyengine/bevy.git", branch = "main", features = ["wayland", "serialize"] }

# Optional remote functionality dependencies
bevy_remote = { git = "https://github.com/bevyengine/bevy.git", branch = "main", optional = true }
serde = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }
ureq = { workspace = true, optional = true }

[lints]
workspace = true

[[example]]
name = "cube_server"
path = "examples/cube_server.rs"
required-features = ["remote"]
88 changes: 88 additions & 0 deletions crates/bevy_entity_inspector/examples/cube_server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! A Bevy app that you can connect to with the BRP and edit.

use bevy::math::ops::cos;
use bevy::{input::common_conditions::input_just_pressed, prelude::*};
use bevy_remote::http::RemoteHttpPlugin;
use bevy_remote::RemotePlugin;
use serde::{Deserialize, Serialize};

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(RemotePlugin::default())
.add_plugins(RemoteHttpPlugin::default())
.add_systems(Startup, setup)
.add_systems(Update, remove.run_if(input_just_pressed(KeyCode::Space)))
.add_systems(Update, move_cube)
// New types must be registered in order to be usable with reflection.
.register_type::<Cube>()
.register_type::<TestResource>()
.run();
}

fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// circular base
commands.spawn((
Mesh3d(meshes.add(Circle::new(4.0))),
MeshMaterial3d(materials.add(Color::WHITE)),
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
));

// cube
commands.spawn((
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
Transform::from_xyz(0.0, 0.5, 0.0),
Cube(1.0),
));

// test resource
commands.insert_resource(TestResource {
foo: Vec2::new(1.0, -1.0),
bar: false,
});

// light
commands.spawn((
PointLight {
shadows_enabled: true,
..default()
},
Transform::from_xyz(4.0, 8.0, 4.0),
));

// camera
commands.spawn((
Camera3d::default(),
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
));
}

/// An arbitrary resource that can be inspected and manipulated with remote methods.
#[derive(Resource, Reflect, Serialize, Deserialize)]
#[reflect(Resource, Serialize, Deserialize)]
pub struct TestResource {
/// An arbitrary field of the test resource.
pub foo: Vec2,

/// Another arbitrary field.
pub bar: bool,
}

fn move_cube(mut query: Query<&mut Transform, With<Cube>>, time: Res<Time>) {
for mut transform in &mut query {
transform.translation.y = -cos(time.elapsed_secs()) + 1.5;
}
}

fn remove(mut commands: Commands, cube_entity: Single<Entity, With<Cube>>) {
commands.entity(*cube_entity).remove::<Cube>();
}

#[derive(Component, Reflect, Serialize, Deserialize)]
#[reflect(Component, Serialize, Deserialize)]
struct Cube(f32);
30 changes: 30 additions & 0 deletions crates/bevy_entity_inspector/examples/inspector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! A Bevy app that demonstrates the Entity Inspector plugin.
//!
//! This example shows how to use the [`InspectorPlugin`] with modular data sources.
//! The inspector can be configured to use different data sources:
//! - Remote mode: Connect to a remote Bevy application via [`bevy_remote`](https://docs.rs/bevy_remote/latest/bevy_remote/)
//! - Scene files: Load and inspect scene data
//! - BSN: Load and inspect BSN (Bevy Scene Notation) files
//!
//! To run this example:
//! - With remote data source: `cargo run --example inspector --features remote`
//! - Basic inspector (empty until data source is configured): `cargo run --example inspector`
//!
//! For remote mode, first start the `cube_server` example:
//! `cargo run --example cube_server --features remote`
//!
//! # Related Documentation
//!
//! - [`bevy_entity_inspector::InspectorPlugin`] - Main plugin for the inspector
//! - [`bevy_entity_inspector::create_dark_inspector_theme`] - Dark theme configuration

use bevy::prelude::*;
use bevy_entity_inspector::{create_dark_inspector_theme, InspectorPlugin};

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(InspectorPlugin)
.insert_resource(create_dark_inspector_theme())
.run();
}
Loading
Loading