use bevy::{ asset::diagnostic::AssetCountDiagnosticsPlugin, diagnostic::{ DiagnosticsStore, EntityCountDiagnosticsPlugin, FrameTimeDiagnosticsPlugin, SystemInformationDiagnosticsPlugin, }, input::{keyboard::KeyboardInput, ButtonState}, utils::{hashbrown::hash_map::Iter, HashMap}, }; use crate::prelude::*; pub(crate) struct DebugPlugin; impl Plugin for DebugPlugin { fn build(&self, app: &mut App) { app.add_plugins(( FrameTimeDiagnosticsPlugin, EntityCountDiagnosticsPlugin::default(), AssetCountDiagnosticsPlugin::::default(), AssetCountDiagnosticsPlugin::::default(), AssetCountDiagnosticsPlugin::::default(), AssetCountDiagnosticsPlugin::::default(), SystemInformationDiagnosticsPlugin::default(), )) .init_resource::() .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 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 .iter() .filter( |KeyboardInput { state, key_code, .. }| *state == ButtonState::Pressed && *key_code == Some(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.with_a(0.6).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.name, 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)); }); }