use bevy::{prelude::*, reflect::ReflectMut};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_resource::<SelectedComponent>()
.add_systems(Startup, setup)
.add_systems(
Update,
(select_component_to_modify, modify_selected_component),
)
.run();
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
commands.spawn(Sprite {
image: asset_server.load("branding/bevy_logo_dark.png"),
..Default::default()
});
let instructions = "\
Press 'T' to select the Transform component's y-translation for modification
Press 'S' to select the Sprite component's alpha value for modification
Press 'Up Arrow' to increase the selected component's value
Press 'Down Arrow' to decrease the selected component's value"
.to_string();
commands.spawn((
Text::new(instructions),
Node {
position_type: PositionType::Absolute,
top: px(12),
left: px(12),
..default()
},
));
}
#[derive(Resource, Default, Clone)]
enum SelectedComponent {
#[default]
Transform,
Sprite,
}
fn select_component_to_modify(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut selected: ResMut<SelectedComponent>,
) {
if keyboard_input.just_pressed(KeyCode::KeyT) {
*selected = SelectedComponent::Transform;
info!("Selected Transform component for modification");
} else if keyboard_input.just_pressed(KeyCode::KeyS) {
*selected = SelectedComponent::Sprite;
info!("Selected Sprite component for modification");
}
}
fn modify_selected_component(world: &mut World) {
let button_input = world.resource::<ButtonInput<KeyCode>>();
let direction_of_modification = if button_input.pressed(KeyCode::ArrowUp) {
1.0
} else if button_input.pressed(KeyCode::ArrowDown) {
-1.0
} else {
return; };
let selected = world.resource::<SelectedComponent>().clone();
let mut sprite_query = world.query_filtered::<Entity, With<Sprite>>();
let entity = sprite_query.iter(world).next().unwrap();
let type_name = match selected {
SelectedComponent::Transform => "bevy_transform::components::transform::Transform",
SelectedComponent::Sprite => "bevy_sprite::sprite::Sprite",
};
let app_registry = world.resource::<AppTypeRegistry>().clone();
let type_id = app_registry
.read()
.get_with_type_path(type_name)
.expect("Type was not registered, or its full path was ambiguous")
.type_id();
let mut reflected_component: Mut<dyn Reflect> = world.get_reflect_mut(entity, type_id).unwrap();
match selected {
SelectedComponent::Sprite => {
let downcast_sprite: &mut Sprite =
reflected_component.downcast_mut::<Sprite>().unwrap();
let color = &mut downcast_sprite.color;
let new_alpha = (color.alpha() + 0.01 * direction_of_modification).clamp(0.0, 1.0);
color.set_alpha(new_alpha);
}
SelectedComponent::Transform => {
let reflect_mut: ReflectMut<'_> = reflected_component.reflect_mut();
let ReflectMut::Struct(struct_mut) = reflect_mut else {
error!("Expected the Transform component type to be a struct");
return;
};
let translation_field = struct_mut.field_mut("translation").unwrap();
let ReflectMut::Struct(translation_struct) = translation_field.reflect_mut() else {
error!("Expected the translation field to be a struct");
return;
};
let y_field: &mut dyn PartialReflect = translation_struct.field_mut("y").unwrap();
let field_type_id = y_field
.get_represented_type_info()
.expect("Found a dynamic type unexpectedly")
.type_id();
let add_assign_trait_data = app_registry
.read()
.get_type_data::<ReflectAddAssign>(field_type_id)
.expect("f32 failed to register ReflectAddAssign")
.clone();
let y_field: &mut dyn Reflect = y_field.try_as_reflect_mut().expect(
"Found a dynamic type unexpectedly, but we need a concrete type to modify it",
);
const MAGNITUDE_OF_MODIFICATION: f32 = 10.;
let delta = direction_of_modification * MAGNITUDE_OF_MODIFICATION;
let boxed_delta: Box<dyn PartialReflect> = Box::new(delta);
add_assign_trait_data
.add_assign(y_field, boxed_delta)
.expect("We cheated and know the types match, so this should always succeed.");
}
}
}