A small, plugin-less utility library making it easier to work with reflection in bevy.
Warning
UNDER DEVELOPMENT, EXPECT BREAKING CHANGES
This library was written to build re-usable UI widgets.
Reflection code is usually very verbose and hard to follow. The functions in this library only require a ReflectTarget
pointing to a field and an &mut World
, and return an easy-to-handle Result<T, ReflectError>
.
The menu (cargo run --example menu
)
example demonstrates a few simple UI widgets used on a settings page.
use bevy::prelude::*;
use bevy_reflect_utils::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_resource::<ExampleResource>()
.add_systems(Startup, setup)
// IMPORTANT: The types you want to operate on must be registered
.register_type::<ExampleResource>()
.run();
}
// IMPORTANT: The types you operate on must derive `Reflect`
#[derive(Resource, Reflect, Debug, Default)]
#[reflect(Resource, Default, Debug)]
pub struct ExampleResource {
value: bool,
}
fn setup(world: &mut World) {
// Define a `ReflectTarget` pointing to `ExampleResource::value`
let target = ReflectTarget::new_resource::<ExampleResource>("value");
// Read the initial value
let initial_value = target.read_value::<bool>(world).unwrap();
println!("initial value: {}", initial_value);
// Set a new value
target.set_value(world, !initial_value).unwrap();
// Read the new value
let new_value = target.read_value::<bool>(world).unwrap();
println!("new value: {}", new_value);
}
This example will print the following:
initial value: false
new value: true
You can run this same example with:
cargo run --example simple
Use Commands
to perform reflection when possible. Use exclusive systems
when you can't avoid it.
For example, to update a value when a button is clicked:
fn handle_click_events(mut commands: &mut Commands) {
// if button was clicked...
let target = ReflectTarget::new_resource::<ExampleResource>("value");
commands.add(move |world: &mut World| {
match target.set_value(world, true) {
Ok(ReflectSetSuccess::Changed) => info!("Success"),
Ok(ReflectSetSuccess::NoChanges) => warn!("Value not changed"),
Err(err) => error!("{err:?}"),
}
});
}
Create a ReflectTarget
referencing a field on an Entity
and Component
:
let target = ReflectTarget::new_component::<ExampleComponent>(entity, "value");
Create a ReflectTarget
referencing a field on a Resource
:
let target = ReflectTarget::new_resource::<ExampleResource>("value");
ReflectTarget
provides the following operations:
Requires knowing the underlying type.
target.read_value::<f32>(world);
Return Value:
Result<f32, ReflectError>
Requires knowing the underlying type.
target.set_value(world, 0.5);
Return Value:
Result<ReflectSetSuccess, ReflectError>
Toggle between the previous/next enum variants.
Also works with data variants, provided the variant implements and reflects Default
.
Does not require knowing the underlying type.
Important: Does not wrap around when reaching the beginning or end of the list of variants.
target.toggle_enum_variant(world, EnumDirection::Forward);
target.toggle_enum_variant(world, EnumDirection::Backward);
Return Value:
Result<ReflectSetSuccess, ReflectError>
Does not require knowing the underlying type.
target.read_enum_variant_name(world);
Return Value:
Result<String, ReflectError>
Does not require knowing the underlying type.
target.read_value_serialized(world);
Return Value:
Result<String, ReflectError>
Example:
Ok("{\"f32\":0.5}")
Does not require knowing the underlying type.
target.set_value_serialized(world, "{\"f32\":0.5}".to_string());
Return Value:
Result<ReflectSetSuccess, ReflectError>
Does not require knowing the underlying type.
target.partial_eq_serialized(world, "{\"f32\":0.5}".to_string());
Return Value:
Result<bool, ReflectError>
The primary error type is ReflectError
.
Most operations that set a value have the following return type:
Result<ReflectSetSuccess, ReflectError>
Where ReflectSetSuccess
allows you know whether the field was changed by the operation:
pub enum ReflectSetSuccess {
Changed,
NoChanges,
}
bevy_reflect_utils |
bevy |
---|---|
0.2 |
0.14 |
0.1 |
0.13 |
Dual-licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.