use crate::prelude::*; pub(crate) struct DebugPlugin; impl Plugin for DebugPlugin { fn build(&self, app: &mut App) { app.add_plugins(( FrameTimeDiagnosticsPlugin, EntityCountDiagnosticsPlugin, SystemInformationDiagnosticsPlugin, )) .init_resource::() // .insert_resource(GizmoConfig { // depth_bias: -0.1, // ..default() // }) .add_systems(Update, (aabb_gizmo,)) // Systems that run in the editor mode .add_systems( Update, ( selected_gizmo.run_if(any_with_component::), selected_position.run_if(any_with_component::), ) .run_if(resource_exists::), ) .add_systems(Startup, init_debug_ui) .add_systems( Update, ( toggle_debug_mode.run_if(on_event::()), display_diagnostics.run_if(resource_exists::), toggle_debug_ui.run_if(resource_changed_or_removed::()), ), ); } } #[derive(Debug, Default, Resource)] pub(crate) struct DebugInfo(HashMap); impl DebugInfo { pub fn set(&mut self, key: String, val: String) -> Option { self.0.insert(key, val) } pub fn _get(&self, key: &String) -> Option<&String> { self.0.get(key) } pub fn _clear(&mut self, key: String) { self.0.remove(&key); } pub fn iter(&self) -> Iter<'_, String, String> { self.0.iter() } } /// Marker resource used to enable Debug mode when present #[derive(Debug, Resource, Default)] pub(crate) struct DebugEnabled; #[derive(Debug, Component)] struct DebugRoot; fn toggle_debug_mode( mut events: EventReader, enabled: Option>, mut commands: Commands, ) { events .read() .filter( |KeyboardInput { state, key_code, .. }| *state == ButtonState::Pressed && *key_code == KeyCode::F3, ) .for_each(|_| match enabled { Some(_) => commands.remove_resource::(), None => commands.insert_resource(DebugEnabled), }); } fn toggle_debug_ui( mut visibility: Query<&mut Visibility, With>, enabled: Option>, ) { visibility.iter_mut().for_each(|mut vis| { *vis = match enabled { Some(_) => Visibility::Visible, None => Visibility::Hidden, } }); } fn init_debug_ui(mut commands: Commands) { commands .spawn(( NodeBundle { style: Style { padding: UiRect::all(Val::Px(10.0)), ..default() }, background_color: Color::BLACK.into(), visibility: Visibility::Hidden, ..default() }, DebugRoot, )) .with_children(|parent| { parent.spawn(( TextBundle { style: Style { ..default() }, ..default() }, DebugRoot, )); }); } fn display_diagnostics( mut root: Query<&mut Text, With>, diagnostics: Res, debug_infos: Res, ) { root.iter_mut().for_each(|mut text| { text.sections = diagnostics .iter() .map(|d| { format!( "{}: {:.0}\n", d.path().as_str(), d.smoothed().unwrap_or(0.0), ) }) .chain(debug_infos.iter().map(|(k, v)| format!("{}: {}\n", k, v))) .map(|s| TextSection::new(s, TextStyle { ..default() })) .collect(); text.sections.sort_unstable_by(|a, b| a.value.cmp(&b.value)); }); } fn aabb_gizmo( added: Query>, mut removed: RemovedComponents, selected: Query>, active: Option>, mut commands: Commands, ) { added.iter().for_each(|e| { commands.entity(e).insert(ShowAabbGizmo { color: Some(Color::RED), }); }); removed.read().for_each(|e| { commands.entity(e).remove::(); }); match active { Some(_) => selected.iter().for_each(|e| { commands.entity(e).insert(ShowAabbGizmo { color: Some(Color::RED), }); }), None => selected.iter().for_each(|e| { commands.entity(e).remove::(); }), } } /// Draw a gizmo showing cardinal directions for a selected object fn selected_gizmo(selected: Query<&GlobalTransform, With>, mut gizmos: Gizmos) { selected.iter().for_each(|g| { let s = g.translation(); gizmos.ray(s, Vec3::X, Color::RED); gizmos.ray(s, Vec3::Y, Color::GREEN); gizmos.ray(s, Vec3::Z, Color::BLUE); }); } fn selected_position( selected: Query<(Entity, &GlobalTransform), With>, mut debug_info: ResMut, ) { let val = selected .iter() .map(|(e, gt)| format!("\n{:?} {:?}", e, gt.translation())) .collect::>() .join(""); debug_info.set("Position".into(), val); }