Print states in debug menu

main
Elijah Voigt 2 days ago
parent f3128b9c38
commit 2376e05109

@ -236,7 +236,7 @@ fn toggle_debug_state(
#[derive(Default, Resource)]
pub struct Notice(pub String);
impl Display for Notice {
impl fmt::Display for Notice {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "{}", self.0)
}
@ -256,7 +256,7 @@ impl ToolTip {
}
}
impl Display for ToolTip {
impl fmt::Display for ToolTip {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
for (k, v) in self.0.iter() {
writeln!(f, "{k}: {v}")?
@ -474,7 +474,7 @@ fn toggle_aabb_gizmo(
#[derive(Resource, Default, Debug)]
struct Fps(f32);
impl Display for Fps {
impl fmt::Display for Fps {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "FPS: {:0.1}", self.0)
}
@ -499,7 +499,7 @@ fn track_fps(time: Res<Time>, mut fps: ResMut<Fps>, mut history: Local<VecDeque<
#[derive(Resource, Default, Debug)]
struct EntityCount(usize);
impl Display for EntityCount {
impl fmt::Display for EntityCount {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "Entities: {}", self.0)
}
@ -517,7 +517,7 @@ struct WindowInfo {
mode: String,
}
impl Display for WindowInfo {
impl fmt::Display for WindowInfo {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(
f,

@ -12,7 +12,7 @@ mod version;
// Rust stdlib
pub use core::time::Duration;
pub use std::{collections::VecDeque, f32::consts::PI, fmt::Display};
pub use std::{collections::VecDeque, f32::consts::PI, fmt};
// Community libraries
pub use bevy::{

@ -156,12 +156,12 @@ fn add_ui_node(
/// Marker component for handling Resource -> Ui Sync
#[derive(Component, Default, Debug)]
pub struct SyncResource<R: Resource + Default + Display>(R);
pub struct SyncResource<R: Resource + Default + fmt::Display>(R);
/// Sync a generic resource to the UI
///
/// Mostly useful for quick n' dirty getting data to the user
pub fn sync_resource_to_ui<R: Resource + Default + Display>(
pub fn sync_resource_to_ui<R: Resource + Default + fmt::Display>(
mut q: Query<(&mut Text, &mut Visibility), With<SyncResource<R>>>,
r: Res<R>,
) {
@ -171,14 +171,30 @@ pub fn sync_resource_to_ui<R: Resource + Default + Display>(
});
}
#[derive(Component, Default, Debug)]
pub struct SyncState<S: States + Default + fmt::Display>(S);
/// Sync a state resource to the UI
///
/// Mostly useful for quick n' dirty getting data to the user
pub fn sync_state_to_ui<S: States + Default + fmt::Display>(
mut q: Query<(&mut Text, &mut Visibility), With<SyncState<S>>>,
s: Res<State<S>>,
) {
q.iter_mut().for_each(|(mut t, mut v)| {
t.0 = format!("{}", s.get());
*v = Visibility::Inherited;
});
}
/// Marker component for handling Resource -> Ui Sync
#[derive(Component, Default, Debug)]
pub struct SyncSingleton<C: Component + Default + Display>(C);
pub struct SyncSingleton<C: Component + Default + fmt::Display>(C);
/// Sync a singleton entity's component to the UI
///
/// Mostly useful for quick n' dirty getting data to the user
pub fn sync_singleton_to_ui<C: Component + Default + Display>(
pub fn sync_singleton_to_ui<C: Component + Default + fmt::Display>(
mut q: Query<(&mut Text, &mut Visibility), With<SyncSingleton<C>>>,
c: Single<&C>,
) {

@ -1,3 +1,6 @@
use bevy::input_focus::tab_navigation::TabGroup;
use engine::theme::ThemedText;
use super::*;
/// Debug UI for Tetris
@ -14,34 +17,76 @@ pub struct DebugPlugin;
impl Plugin for DebugPlugin {
fn build(&self, app: &mut App) {
app
.init_state::<Debugger>()
.init_state::<DebugState>()
.add_systems(Startup, setup_ui)
.add_systems(Update,
// Logging state transitions
(
log_transition::<Debugger>.run_if(state_changed::<Debugger>),
log_transition::<DebugState>.run_if(state_changed::<DebugState>),
log_transition::<Loading>.run_if(state_changed::<Loading>),
log_transition::<GameState>.run_if(state_changed::<GameState>),
))
.add_systems(Update, toggle_debug.run_if(input_just_pressed(KeyCode::F12)));
.add_systems(Update, toggle_debug.run_if(input_just_pressed(KeyCode::F12)))
.add_systems(Update,
(
toggle_state_visibility::<DebugState>,
sync_state_to_ui::<DebugState>,
).run_if(state_changed::<DebugState>)
);
}
}
/// Tracks if the game is in debug mode
#[derive(States, Default, Clone, Eq, Debug, PartialEq, Hash)]
pub enum Debugger {
#[default]
Off,
On,
#[derive(States, Default, Clone, Eq, Debug, PartialEq, Hash, Component)]
pub struct DebugState(bool);
impl From<bool> for DebugState {
fn from(b: bool) -> Self {
DebugState(b)
}
}
impl fmt::Display for DebugState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DebugState {}", self.0)
}
}
/// Setup the debugger UI
fn setup_ui(
mut commands: Commands,
) {
commands.spawn(
(toggle_switch((),), observe(checkbox_self_update)),
);
commands.spawn((
Node {
display: Display::Flex,
flex_direction: FlexDirection::Row,
align_items: AlignItems::Center,
justify_content: JustifyContent::Start,
column_gap: px(8),
..default()
},
TabGroup::default(),
children![
(Text::new("debugger:"), ThemedText),
(toggle_switch((),), observe(checkbox_self_update), observe(debug_toggle)),
],
));
commands.spawn((
Node {
bottom: px(0.0),
left: px(0.0),
position_type: PositionType::Absolute,
flex_direction: FlexDirection::Column,
..default()
},
DebugState(true),
children![
(Text::new("DebugState State"), SyncState::<DebugState>::default()),
(Text::new("Loading State"), SyncState::<Loading>::default()),
(Text::new("Game State"), SyncState::<GameState>::default()),
]
));
}
/// Logs all state transitions for state T
@ -58,13 +103,13 @@ fn log_transition<T: States + PartialEq + Clone>(
/// Toggle the debug state when a key is pressed
fn toggle_debug(
curr: Res<State<Debugger>>,
mut next: ResMut<NextState<Debugger>>,
curr: Res<State<DebugState>>,
mut next: ResMut<NextState<DebugState>>,
) {
next.set(
match curr.get() {
Debugger::On => Debugger::Off,
Debugger::Off => Debugger::On,
}
);
next.set(DebugState(!curr.get().0));
}
fn debug_toggle(event: On<ValueChange<bool>>, mut next_state: ResMut<NextState<DebugState>>) {
info!("Debug State Toggled: {:?}", event.event().value);
next_state.set(event.event().value.into());
}

@ -23,28 +23,41 @@ fn main() {
.add_systems(
Update,
loading_check
.run_if(in_state(Loading::Active))
.run_if(in_state(Loading(true)))
.run_if(resource_changed::<AllAssets>),
)
// Wait for pending assets to be loaded
.add_systems(Update, loading_wait.run_if(in_state(Loading::Active)))
.add_systems(Update, loading_wait.run_if(in_state(Loading(true))))
// Once done loading, move to the setup state
.add_systems(OnEnter(Loading::Idle), setup_game)
.add_systems(OnEnter(Loading(false)), setup_game)
// Check if the game is ready to progress
.add_systems(Update, setup_wait.run_if(in_state(GameState::Setup)))
// State toggles
.add_systems(Update, (
(
toggle_state_visibility::<Loading>,
sync_state_to_ui::<Loading>,
).run_if(state_changed::<Loading>),
(
toggle_state_visibility::<GameState>,
sync_state_to_ui::<GameState>,
).run_if(state_changed::<GameState>)
))
.run();
}
/// Reports if the game is loading assets
#[derive(States, Default, Clone, Eq, Debug, PartialEq, Hash)]
enum Loading {
#[default]
Active,
Idle,
#[derive(States, Default, Clone, Eq, Debug, PartialEq, Hash, Component)]
struct Loading(bool);
impl fmt::Display for Loading {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
rite!(f, "Loading {}", self.0)
}
}
/// Tracks what state the main game loop is in
#[derive(States, Default, Clone, Eq, Debug, PartialEq, Hash)]
#[derive(States, Default, Clone, Eq, Debug, PartialEq, Hash, Component)]
enum GameState {
#[default]
Boot,
@ -52,6 +65,16 @@ enum GameState {
Run,
}
impl fmt::Display for GameState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GameState::Boot => write!(f, "Game State: Boot"),
GameState::Setup => write!(f, "GameState: Setup"),
GameState::Run => write!(f, "GameState: Run"),
}
}
}
/// A list of all assets so we don't lose them
#[derive(Default, Resource, Debug)]
struct AllAssets {
@ -77,20 +100,20 @@ fn loading_check(mut next: ResMut<NextState<Loading>>, all_assets: Res<AllAssets
next.set(Loading::Active);
}
/// Waits in Loading::Active until all assets are loaded then move to Loading::Idle
/// Waits in Loading::Active until all assets are loaded then move to Loading(false)
fn loading_wait(
curr: Res<State<Loading>>,
mut next: ResMut<NextState<Loading>>,
server: Res<AssetServer>,
all_assets: Res<AllAssets>,
) {
debug_assert!(*curr.get() == Loading::Active);
debug_assert!(*curr.get() == Loading(true));
if all_assets
.handles
.iter()
.all(|h| matches!(server.get_load_state(h.id()), Some(LoadState::Loaded)))
{
next.set(Loading::Idle);
next.set(Loading(false));
}
}

Loading…
Cancel
Save