bevy-inspector-egui v0.15.0-pre.0 Announcement and Migration Guide

I just released the next version 0.15.0-pre.0 of bevy-inspector-egui and would like to gather some feedback before the actual release.
If you just want a stable release for bevy 0.9, feel free to continue using bevy-inspector-egui 0.14.

If you miss any features from the previous release, or have suggestions for features or API design, please open an issue at bevy-inspector-egui/issues or ping me on the bevy discord (in #project-collaboration @dubi steinkek#2182).

You can check out the 0.15.0 docs on docs.rs: https://docs.rs/bevy-inspector-egui/0.15.0-pre

What’s new in the update?

This release features the removal of the Inspectable trait in favor of reusing the existing Reflect trait. This leads to less derive boilerplate and more reusability, paving the way for non-egui Inspector implementations (in the general sense, could also be autocomplete in a console) and usage outside of bevy.

New APIs

The release is a rewrite from scratch so here’s the new layout of the crate. For more detail, you can browse the crate docs.

The main modules are:
egui_reflect_inspector - General-purpose machinery for displaying Reflect types using egui
bevy_inspector - Methods for displaying bevy resources, assets and entities
inspector_options - Way of associating options to fields using InspectorOptions
quick - Easy plugins for showing UI panels.

egui_reflect_inspector

This module has no knowledge of bevy and provides general purpose methods for displaying arbitrary &mut dyn Reflect or &dyn Reflect (that’s right, we support readonly UI now).

Most of the time however you can just use either the

use bevy_reflect::{Reflect, TypeRegistry}; use bevy_inspector_egui::egui_reflect_inspector::{InspectorUi, Context}; #[derive(Reflect)] struct Data { value: f32, } fn ui(data: &mut Data, ui: &mut egui::Ui, type_registry: &TypeRegistry) -> bool { // empty context, with no access to the bevy world let mut cx = Context::default(); // no short circuiting, couldn't display `Handle<StandardMaterial>` let mut env = InspectorUi::new_no_short_circuit(type_registry, &mut cx); env.ui_for_reflect(data, ui) } // alternatively, just call // `egui_reflect_inspector::ui_for_value(&mut data, ui, type_registry)` // which does the same.

This is the minimum needed to display a value: the value, the Ui and the type registry. This is enough for basic usage, but consider a struct like this

pub struct StandardMaterial { base_color: Color base_color_texture: Option<Handle<Image>> .. }

base_color can be reflected and displayed, but what about the Handle<Image>? To really display its value, you would need to access the Assets<Image> resource and look the handle up.
This is what the Context struct and the short_circuiting is about.
You can put a &mut World into the Context and InspectorUi::new(type_registry, cx, short_circuit, short_circuit_readonly) accepts to short circuiting methods, which will be asked at every step in the recursive reflect walking if you know a better way to display the struct.

We provide short circuiting methods capable of displaying Handles which look something like this:

fn short_circuit_handle(..) { if value is Handle<?> { let assets = context.world.resource::Assets<?asset_type>(); let value = assets.get_mut(handle); return ui_for_reflect(value, ..); } return None; // no short circuiting, continue normally }

As a user, you don’t have to worry about this, if you just use the InspectorUi::for_bevy constructor:

let world: &mut World = ..; let standard_material: Handle<StandardMaterial> = ..; let mut cx = Context { world: Some(world.into()), }; let mut env = InspectorUi::for_bevy(type_registry, &mut cx); env.ui_for_reflect(value, ui)

This will just work and display the value of the StandardMaterial.

Side node about change detection

If you have a Mut value and directly write env.ui_for_reflect(&mut *value, ui) you will always trigger change detection.
The proper way to write this is

let changed = env.ui_for_reflect(value.bypass_change_detection(), ui); if changed { value.set_changed(); }

bevy_inspector

This module contains functions for quickly building UI for bevy apps.
The following example speaks for itself, for more info just look at the module docs.

use bevy_inspector_egui::bevy_inspector; #[derive(Debug, Clone, Eq, PartialEq, Hash, Reflect)] enum AppState { A, B, C } fn show_ui(world: &mut World, ui: &mut egui::Ui) { ui.heading("Msaa resource"); bevy_inspector::ui_for_resource::<Msaa>(world, ui); ui.heading("App State"); bevy_inspector::ui_for_state::<AppState>(world, ui); egui::CollapsingHeader::new("Entities") .default_open(true) .show(ui, |ui| { bevy_inspector::ui_for_world_entities(world, ui); }); egui::CollapsingHeader::new("Resources").show(ui, |ui| { bevy_inspector::ui_for_resources(world, ui); }); egui::CollapsingHeader::new("Assets").show(ui, |ui| { bevy_inspector::ui_for_all_assets(world, ui); }); }

quick

Plugins you can easily add to your app.

Pro: less typing
Con: less flexibility

#[derive(Reflect, Resource, Default, InspectorOptions)] #[reflect(Resource, InspectorOptions)] struct Configuration { #[inspector(min = 0.0)] value: f32, } #[derive(Reflect, Debug, Clone, Eq, PartialEq, Hash)] enum AppState { A, B, C } fn main() { App::new() .add_plugins(DefaultPlugins) // window with UI for the entire world, with a tab for entities, resources and assets .add_plugin(WorldInspectorPlugin) // window with UI for a single resource .add_plugin(ResourceInspectorPlugin::<Configuration>::default()) // window with UI for your app state .add_plugin(StateInspectorPlugin::<AppState>::default()) .add_state(AppState::A) .init_resource::<Configuration>() .register_type::<Configuration>() .register_type::<AppState>() .add_startup_system(setup) .run(); }

If you want more control over the presentation (egui window vs side panel vs egui_dock), or over the content, you can just add an exclusive system like this:

fn main() { App::new() .add_plugins(DefaultPlugins) // if you don't use a `quick` plugin, you'll have to add the `DefaultInspectorConfigPlugin` yourself. // it registers UI functions for all primitive types and // adds default inspector options (e.g. value ranges for the `StandardMaterial`) .add_plugin(DefaultInspectorConfigPlugin) .add_plugin(EguiPlugin) .add_system(ui) .run(); } fn ui(world: &mut World) { let egui_context = world .resource_mut::<bevy_egui::EguiContext>() .ctx_mut() .clone(); egui::Window::new("UI").show(&egui_context, |ui| { egui::ScrollArea::vertical().show(ui, |ui| { bevy_inspector_egui::bevy_inspector::ui_for_world(world, ui); // allow resizing beyond the actual UI ui.allocate_space(ui.available_size()); }); }); }

Migration Guide

WorldInspectorPlugin

Before

use bevy_inspector_egui::WorldInspectorPlugin; fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(WorldInspectorPlugin) .run(); }

After

use bevy_inspector_egui::quick::WorldInspectorPlugin; fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(WorldInspectorPlugin) .run(); }

Basic usage of the world inspector doesn’t actually change. The only difference is that registration works differently now and WorldInspectorParams is gone. If you want to have more customization, copy the implementation, it’s ~10 lines.

InspectorPlugin

To display a single resource, the InspectorPlugin has been replaced with the ResourceInspectorPlugin:

Before

#[derive(Resource, Inspectable, Default)] struct Data { the: f32, values: String, } fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(InspectorPlugin::<Data>::new()) .run(); }

After

// replace `Inspectable` with `Reflect` #[derive(Reflect, Resource, Default)] #[reflect(Resource)] struct Data { the: f32, values: String, } fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(ResourceInspectorPlugin::<Data>::new()) .init_type::<Default>() // the `ResourceInspectorPlugin` doesn't insert the resource .register_type::<Data>() .run(); }

#[inspectable] Attributes

Before

The Reflect trait is not capable of reading and storing arbitrary attributes. Instead, you can add the InspectorOptions derive which will parse and typecheck the attributes, and insert a type-erased options struct into the type registry.

#[derive(Resource, Inspectable)] struct Data { #[inspectable(min = 10.0, max = 70.0)] font_size: f32, }

After

#[derive(Reflect, Resource, InspectorOptions)] #[reflect(Resource, InspectorOptions)] // don't forget include the `InspectorOptions` here struct Data { #[inspector(min = 10.0, max = 70.0)] font_size: f32, } ... app.register_type::<Data>() // will register `ReflectInspectorOptions` in the type registry



As I said in the beginning, if you have any feedback please open an issue or ping me on discord :)