use bevy::window::WindowResized; 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::() .init_state::() .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::), debug_piece.run_if(resource_changed::), ) .run_if(in_state(DebugState::Enabled)), ) .add_systems(Startup, init_debug_ui) .add_systems( Update, ( toggle_debug_mode.run_if(on_event::()), display_diagnostics.run_if(in_state(DebugState::Enabled)), toggle_debug_ui.run_if(state_changed::), aspect_ratio.run_if(on_event::()), ), ); } } #[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(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)] pub(crate) enum DebugState { #[default] Disabled, Enabled, } #[derive(Debug, Component)] struct DebugRoot; #[derive(Debug, Component)] struct DebugPiece; fn toggle_debug_mode( mut events: EventReader, current_state: Res>, mut next_state: ResMut>, ) { events .read() .filter( |KeyboardInput { state, key_code, .. }| *state == ButtonState::Pressed && *key_code == KeyCode::F3, ) .for_each(|_| match current_state.get() { DebugState::Enabled => next_state.set(DebugState::Disabled), DebugState::Disabled => next_state.set(DebugState::Enabled), }); } fn toggle_debug_ui( mut visibility: Query<&mut Visibility, Or<(With, With)>>, current_state: Res>, ) { visibility.iter_mut().for_each(|mut vis| { *vis = match current_state.get() { DebugState::Enabled => Visibility::Visible, DebugState::Disabled => 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, )); }); commands .spawn(( NodeBundle { style: Style { padding: UiRect::all(Val::Px(10.0)), position_type: PositionType::Absolute, bottom: Val::Px(0.0), right: Val::Px(0.0), ..default() }, background_color: Color::BLACK.into(), visibility: Visibility::Hidden, ..default() }, DebugPiece, )) .with_children(|parent| { parent.spawn(( TextBundle { style: Style { ..default() }, ..default() }, DebugPiece, )); }); } 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>, current_state: Res>, 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 current_state.get() { DebugState::Enabled => selected.iter().for_each(|e| { commands.entity(e).insert(ShowAabbGizmo { color: Some(Color::RED), }); }), DebugState::Disabled => 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); } fn debug_piece( query: Query< ( Entity, Option<&Side>, Option<&Piece>, Option<&BoardIndex>, Option<&display3d::Animating>, Option<&Selected>, ), With, >, pointer: Res, mut root: Query<(&mut Text, &mut Visibility), With>, // mut commands: Commands, ) { // query.iter().nth(1).iter().for_each(|e| { // commands.entity(*e).log_components(); // }); match *pointer { display3d::PiecePointer(Some(e)) => { if let Ok((e, si, p, bi, a, sel)) = query.get(e) { root.iter_mut().for_each(|(mut text, mut vis)| { let value = format!("Entity: {:?}\nSide: {:?}\nPiece: {:?}\nBoard Index: {:?}\nAnimating: {:?}\nSelected: {:?}", e, si, p, bi, a, sel); *text = Text::from_section(value, TextStyle { ..default() }); *vis = Visibility::Inherited; }); } } _ => { root.iter_mut().for_each(|(_, mut vis)| { *vis = Visibility::Hidden; }); } } } fn aspect_ratio(mut debug_info: ResMut, window: Query<&Window>) { window.iter().for_each(|window| { let x = window.resolution.width(); let y = window.resolution.height(); let aspect_ratio = format!("{}x{}", x, y); debug_info.set("aspect Ratio".into(), aspect_ratio); }) }