How to use bevy_reflect to get component field values at runtime? #7886
-
I've spent some time looking around the docs, various issues, and reading through the code and can't quite figure out how to do this. For context, I am just messing around building a UI / editor, and am trying to create functionality where you can click on an entity, it shows the entities components, then you can click a component, and it expands to show all reflected fields and their values with the goal of editing those values. As a somewhat distilled example, here is what I have so far. I have another component that builds the ui for a scene-graph, and when you click an entity it sets that Entity type on the ui_state resource I have. This is the system that would draw that selected entity's field info on another panel. #[derive(Debug, Copy, Clone, Serialize, Deserialize, Reflect, FromReflect, Resource)]
#[repr(C)]
pub struct Vector3f{
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Component, Debug, Clone, Serialize, Deserialize, Reflect, FromReflect)]
#[reflect(Component)]
pub struct TransformComponent{
pub global_position: Vector3f,
}
// some time later in the code
let registry_arc = &world.get_resource_mut::<TypeRegistryResource>().unwrap().0;
let mut registry = registry_arc.write();
registry.register::<TransformComponent>();
registry.register::<Vector3f>();
// and here is the system
pub fn EntityInspectionUiSystem(
query: Query<(&EntityInspectorComponent, Entity)>,
ui_state: Res<EditorUiState>,
type_registry_res: Res<TypeRegistryResource>,
world: &World,
){
if let Some(selected_entity: Entity) = ui_state.selected_entity{
// boilerplate to get the proper panel ui here
let mut ui = some_boiler_plate();
ui.heading(format!("Selected Entity {}", selected_entity.to_bits()));
ui.separator();
let archetypes = world.archetypes();
let components = world.components();
for archetype in archetypes.iter() {
if archetype.entities().iter().any(|e| e.entity() == selected_entity) {
for comp_id in archetype.components() {
if let Some(comp_info) = components.get_info(comp_id) {
let component_name = comp_info.name().split("::").last().unwrap();
// at this point we have the component name
egui::CollapsingHeader::new(component_name).show(&mut ui, |ui|{
// I am not convinced I am actually doing this the best way, but it's what I managed to figure out
let registration = type_registry
.get_with_name(comp_info.name())
.unwrap();
let reflect_component = registration.data::<ReflectComponent>().unwrap();
let dyn_reflect = reflect_component.reflect(world, selected_entity).unwrap();
let type_info = dyn_reflect.get_type_info();
match type_info {
TypeInfo::Struct(struct_info) => {
for (i, field) in struct_info.iter().enumerate(){
ui.horizontal(|ui|{
ui.label(field.name());
});
}
},
_ => ();
}
});
}
}
}
}
}
} So things I have considered are building a proxy dynamic struct and adding it to the entity this ui lives on for the duration of time that the component is being edited, reading field names off of the type info, checking if they're equal to certain types using NamedField.is::() etc, then, since I'd know the type at that point, assigning default values to the proxy DynamicStruct, having the ui operate on the proxy struct since you can pull field values off it, then applying that proxy struct to the reflected component on the selected entity. Also if I could somehow cast the &dyn Reflect value or the ReflectComponent into a dynamic struct then I could probably just edit it directly, but couldn't figure out how to do that. That seems more or less fine, but I wasn't super content with the idea of manually checking if a field was any possible type that my fields could be both when I'm building the proxy struct and when I'm reading fields off of it to build specific ui editors, which was what led me to ask this question. If I am going about this completely wrong, I am open to any and all advice. To summarize, I basically am trying to figure out how to edit the fields of any given component on any given entity via a system without knowing the types of the components or fields at compile time. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
Just an update for anyone else who was curious, I was able to solve this by using let reflected = reflect_component.reflect(world, selected_entity).unwrap();
let ReflectRef::Struct(reflected) = reflected.reflect_ref() else { unreachable!() };
for (i, field) in reflected.iter_fields().enumerate() {
ui.label(format!("{:?} : {:?}", reflected.name_at(i).unwrap(), reflected.field_at(i).unwrap()));
} Obviously there's some mutability changes to be made to actually edit the fields, but hopefully this helps anyone trying to do something similar. |
Beta Was this translation helpful? Give feedback.
-
Hi @DumplingEater! Still remember this stuff a year+ later? I want the same thing: to iterate through my entities and get the values in their components. Your post(s) were very helpful--as close as I've gotten. Unfortunately I get the field values for only two of the components--and can't see how to get the values for the others--can't see why I hit the Any ideas? Here's my munge of your code, followed by a grab from the console showing the kind of things I get: always get TypeInfo, but can't always get the ReflectRef for the values.
And I get:
(and more...) |
Beta Was this translation helpful? Give feedback.
Just an update for anyone else who was curious, I was able to solve this by using
ReflectRef
to convert mydyn Reflect
object into the a trait object of the subtraitStruct
, and access the fields that way. Turns out it was literally on the landing page for bevy_reflect docs. Anyways here's the snippet I used:Obviously there's some mutability changes to be made to actually e…