Compare commits

..

6 Commits

Author SHA1 Message Date
Elijah Voigt 125f303ec4 we have drawing from an asset! 3 days ago
Elijah Voigt 1536b43c51 Adding real spawning logic for handle<ShapeAsset> 3 days ago
Elijah Voigt 86436575e2 Simplify binary states 3 days ago
Elijah Voigt 2376e05109 Print states in debug menu 3 days ago
Elijah Voigt f3128b9c38 Toggle debug state with f12 5 days ago
Elijah Voigt 198233236d Toggle debug state with f12 5 days ago

@ -236,7 +236,7 @@ fn toggle_debug_state(
#[derive(Default, Resource)] #[derive(Default, Resource)]
pub struct Notice(pub String); 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 { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "{}", self.0) 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 { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
for (k, v) in self.0.iter() { for (k, v) in self.0.iter() {
writeln!(f, "{k}: {v}")? writeln!(f, "{k}: {v}")?
@ -474,7 +474,7 @@ fn toggle_aabb_gizmo(
#[derive(Resource, Default, Debug)] #[derive(Resource, Default, Debug)]
struct Fps(f32); struct Fps(f32);
impl Display for Fps { impl fmt::Display for Fps {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "FPS: {:0.1}", self.0) 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)] #[derive(Resource, Default, Debug)]
struct EntityCount(usize); struct EntityCount(usize);
impl Display for EntityCount { impl fmt::Display for EntityCount {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "Entities: {}", self.0) writeln!(f, "Entities: {}", self.0)
} }
@ -517,7 +517,7 @@ struct WindowInfo {
mode: String, mode: String,
} }
impl Display for WindowInfo { impl fmt::Display for WindowInfo {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!( writeln!(
f, f,

@ -12,7 +12,7 @@ mod version;
// Rust stdlib // Rust stdlib
pub use core::time::Duration; 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 // Community libraries
pub use bevy::{ pub use bevy::{
@ -39,6 +39,7 @@ pub use bevy::{
render::render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages}, render::render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages},
sprite_render::*, sprite_render::*,
time::common_conditions::*, time::common_conditions::*,
feathers::*,
ui::*, ui::*,
ui_widgets::{Button, *}, ui_widgets::{Button, *},
window::{WindowResized, WindowResolution}, window::{WindowResized, WindowResolution},

@ -156,12 +156,12 @@ fn add_ui_node(
/// Marker component for handling Resource -> Ui Sync /// Marker component for handling Resource -> Ui Sync
#[derive(Component, Default, Debug)] #[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 /// Sync a generic resource to the UI
/// ///
/// Mostly useful for quick n' dirty getting data to the user /// 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>>>, mut q: Query<(&mut Text, &mut Visibility), With<SyncResource<R>>>,
r: Res<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 /// Marker component for handling Resource -> Ui Sync
#[derive(Component, Default, Debug)] #[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 /// Sync a singleton entity's component to the UI
/// ///
/// Mostly useful for quick n' dirty getting data to the user /// 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>>>, mut q: Query<(&mut Text, &mut Visibility), With<SyncSingleton<C>>>,
c: Single<&C>, c: Single<&C>,
) { ) {

@ -12,7 +12,7 @@ impl Plugin for BlocksPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_asset::<ShapeAsset>() app.init_asset::<ShapeAsset>()
.init_asset_loader::<ShapeAssetLoader>() .init_asset_loader::<ShapeAssetLoader>()
.add_systems(OnEnter(Loading::Active), load_assets.run_if(run_once)) .add_systems(OnEnter(Loading(true)), load_assets.run_if(run_once))
.add_systems(OnEnter(GameState::Setup), (setup_camera, setup_blocks)) .add_systems(OnEnter(GameState::Setup), (setup_camera, setup_blocks))
.add_observer(add_shape); .add_observer(add_shape);
} }
@ -23,11 +23,15 @@ impl Plugin for BlocksPlugin {
#[derive(Asset, TypePath, Debug, Deserialize)] #[derive(Asset, TypePath, Debug, Deserialize)]
struct ShapeAsset { struct ShapeAsset {
layout: Vec<Vec<u8>>, layout: Vec<Vec<u8>>,
#[serde(skip)]
mesh: Mesh2d,
#[serde(skip)]
material: MeshMaterial2d<ColorMaterial>,
} }
impl ShapeAsset { impl ShapeAsset {
fn into_bundle(&self) -> impl Bundle { fn into_bundle(&self) -> impl Bundle {
() (self.mesh.clone(), self.material.clone())
} }
} }
@ -50,12 +54,33 @@ impl AssetLoader for ShapeAssetLoader {
&self, &self,
reader: &mut dyn Reader, reader: &mut dyn Reader,
_settings: &(), _settings: &(),
_load_context: &mut LoadContext<'_>, load_context: &mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> { ) -> Result<Self::Asset, Self::Error> {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?; reader.read_to_end(&mut bytes).await?;
let shape_asset = toml::from_slice::<ShapeAsset>(bytes.as_slice())?; // TODO: Create sub-assets for mesh and material
Ok(shape_asset) let mesh = {
// https://docs.rs/bevy/latest/bevy/asset/struct.LoadContext.html#method.add_labeled_asset
let m: Mesh = Rectangle::new(100.0, 100.0).into();
let h: Handle<Mesh> = load_context.add_labeled_asset(format!("{}#mesh", load_context.asset_path()), m);
Mesh2d(h)
};
let material = {
let m = ColorMaterial {
color: PURPLE.into(),
..default()
};
let h: Handle<ColorMaterial> = load_context.add_labeled_asset(format!("{}#material", load_context.asset_path()), m);
MeshMaterial2d(h)
};
let parsed = toml::from_slice::<ShapeAsset>(bytes.as_slice())?;
Ok(
ShapeAsset {
mesh,
material,
..parsed
}
)
} }
fn extensions(&self) -> &[&str] { fn extensions(&self) -> &[&str] {

@ -1,3 +1,6 @@
use bevy::input_focus::tab_navigation::TabGroup;
use engine::theme::ThemedText;
use super::*; use super::*;
/// Debug UI for Tetris /// Debug UI for Tetris
@ -6,28 +9,86 @@ use super::*;
pub struct DebugPlugin; pub struct DebugPlugin;
// Debugging wish-list: // Debugging wish-list:
// - Toggle debug on/off with f12 key
// - Bounding boxes around entities // - Bounding boxes around entities
// - Toggleable in UI
// - Cursor at the center of the world // - Cursor at the center of the world
// - Show current state(s) // - Toggleable in UI
// - Show current state(s) in UI
impl Plugin for DebugPlugin { impl Plugin for DebugPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Update, ( app
log_transition::<Debugger>.run_if(state_changed::<Debugger>), .init_state::<DebugState>()
.add_systems(Startup, setup_ui)
.add_systems(Update,
// Logging state transitions
(
log_transition::<DebugState>.run_if(state_changed::<DebugState>),
log_transition::<Loading>.run_if(state_changed::<Loading>), log_transition::<Loading>.run_if(state_changed::<Loading>),
log_transition::<GameState>.run_if(state_changed::<GameState>), log_transition::<GameState>.run_if(state_changed::<GameState>),
)); ))
.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 /// Tracks if the game is in debug mode
#[derive(States, Default, Clone, Eq, Debug, PartialEq, Hash)] #[derive(States, Default, Clone, Eq, Debug, PartialEq, Hash, Component)]
pub enum Debugger { pub struct DebugState(bool);
#[default]
Off, impl From<bool> for DebugState {
On, 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((
Node {
display: Display::Flex,
flex_direction: FlexDirection::Row,
align_items: AlignItems::Center,
justify_content: JustifyContent::Start,
column_gap: px(8),
..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
fn log_transition<T: States + PartialEq + Clone>( fn log_transition<T: States + PartialEq + Clone>(
curr: Res<State<T>>, curr: Res<State<T>>,
mut prev: Local<Option<T>>, mut prev: Local<Option<T>>,
@ -38,3 +99,16 @@ fn log_transition<T: States + PartialEq + Clone>(
*prev = Some(curr.get().clone()); *prev = Some(curr.get().clone());
} }
/// Toggle the debug state when a key is pressed
fn toggle_debug(
curr: Res<State<DebugState>>,
mut next: ResMut<NextState<DebugState>>,
) {
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());
}

@ -12,9 +12,10 @@ use fighter::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins((DefaultPlugins, BlocksPlugin, FighterPlugin, DebugPlugin)) .add_plugins(DefaultPlugins)
.add_plugins(FeathersPlugins)
.add_plugins((BlocksPlugin, FighterPlugin, DebugPlugin))
.init_state::<Loading>() .init_state::<Loading>()
.init_state::<Debugger>()
.init_state::<GameState>() .init_state::<GameState>()
.init_resource::<AllAssets>() .init_resource::<AllAssets>()
.init_resource::<SetupChecklist>() .init_resource::<SetupChecklist>()
@ -22,28 +23,47 @@ fn main() {
.add_systems( .add_systems(
Update, Update,
loading_check loading_check
.run_if(in_state(Loading::Active)) .run_if(in_state(Loading(true)))
.run_if(resource_changed::<AllAssets>), .run_if(resource_changed::<AllAssets>),
) )
// Wait for pending assets to be loaded // 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 // 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 // Check if the game is ready to progress
.add_systems(Update, setup_wait.run_if(in_state(GameState::Setup))) .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(); .run();
} }
/// Reports if the game is loading assets /// Reports if the game is loading assets
#[derive(States, Default, Clone, Eq, Debug, PartialEq, Hash)] #[derive(States, Clone, Eq, Debug, PartialEq, Hash, Component)]
enum Loading { struct Loading(bool);
#[default]
Active, impl Default for Loading {
Idle, fn default() -> Self {
Loading(true)
}
}
impl fmt::Display for Loading {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Loading {}", self.0)
}
} }
/// Tracks what state the main game loop is in /// 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 { enum GameState {
#[default] #[default]
Boot, Boot,
@ -51,6 +71,16 @@ enum GameState {
Run, 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 /// A list of all assets so we don't lose them
#[derive(Default, Resource, Debug)] #[derive(Default, Resource, Debug)]
struct AllAssets { struct AllAssets {
@ -73,23 +103,23 @@ impl SetupChecklist {
fn loading_check(mut next: ResMut<NextState<Loading>>, all_assets: Res<AllAssets>) { fn loading_check(mut next: ResMut<NextState<Loading>>, all_assets: Res<AllAssets>) {
debug_assert!(all_assets.is_changed()); debug_assert!(all_assets.is_changed());
next.set(Loading::Active); next.set(Loading(true));
} }
/// 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( fn loading_wait(
curr: Res<State<Loading>>, curr: Res<State<Loading>>,
mut next: ResMut<NextState<Loading>>, mut next: ResMut<NextState<Loading>>,
server: Res<AssetServer>, server: Res<AssetServer>,
all_assets: Res<AllAssets>, all_assets: Res<AllAssets>,
) { ) {
debug_assert!(*curr.get() == Loading::Active); debug_assert!(*curr.get() == Loading(true));
if all_assets if all_assets
.handles .handles
.iter() .iter()
.all(|h| matches!(server.get_load_state(h.id()), Some(LoadState::Loaded))) .all(|h| matches!(server.get_load_state(h.id()), Some(LoadState::Loaded)))
{ {
next.set(Loading::Idle); next.set(Loading(false));
} }
} }

Loading…
Cancel
Save