diff --git a/examples/sensors.rs b/examples/sensors.rs index 9e2bc6b..54fc71c 100644 --- a/examples/sensors.rs +++ b/examples/sensors.rs @@ -27,7 +27,7 @@ fn main() { control_ball, process_events, sync_resource_to_ui::.run_if(resource_changed::), - ), + ).run_if(in_state(GameState::Main)), ) .run(); } diff --git a/src/base_game.rs b/src/base_game.rs index 5b9ff67..4c2278c 100644 --- a/src/base_game.rs +++ b/src/base_game.rs @@ -9,10 +9,19 @@ impl Plugin for BaseGamePlugin { .add_plugins(DebuggingPlugin) .add_plugins(MeshPickingPlugin) .add_plugins(RapierPhysicsPlugin::::default()) - .add_systems(Startup, setup_camera); + .add_plugins(LoadingPlugin) + .add_systems(Startup, setup_camera) + .init_state::(); } } +#[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub enum GameState { + #[default] + Loading, + Main, +} + /// System to toggle the visibility of entities based on their state pub fn toggle_state_visibility( mut q: Query<(Entity, &mut Visibility, &S)>, diff --git a/src/lib.rs b/src/lib.rs index e76cca8..9e06e0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,12 +3,14 @@ mod base_game; mod debug; +mod loading; mod scheduling; mod ui; pub use std::fmt::Display; pub use bevy::{ + asset::LoadState, color::palettes::css::{BLACK, RED, WHITE}, input::{ButtonState, keyboard::KeyboardInput, mouse::MouseMotion}, prelude::*, @@ -17,5 +19,6 @@ pub use bevy_rapier3d::prelude::*; pub use base_game::*; pub use debug::*; +pub use loading::*; pub use scheduling::*; pub use ui::*; diff --git a/src/loading.rs b/src/loading.rs new file mode 100644 index 0000000..dd40161 --- /dev/null +++ b/src/loading.rs @@ -0,0 +1,62 @@ + +use super::*; + +/// Systems run during loading state +pub struct LoadingPlugin; + +impl Plugin for LoadingPlugin { + fn build(&self, app: &mut App) { + // PERF: We check all asset classes every frame, should be event based + app + .init_resource::() + .add_systems(PreUpdate, reset_progress) + .add_systems(Update, + ( + track_loading::, + track_loading::, + track_loading::, + track_loading::, + track_loading::, + track_loading::, + ).run_if(in_state(GameState::Loading)), + ) + .add_systems(PostUpdate, check_progress); + } +} + +/// Resource for tracking asset loading progress +#[derive(Resource, Default)] +struct TrackLoadingProgress(Vec); + +/// At the start of the update clear progress +fn reset_progress( + mut progress: ResMut, +) { + progress.0.clear() +} + +/// Track the progress of all assets in this asset class +fn track_loading( + assets: Res>, + server: Res, + mut progress: ResMut, +) { + let all_loaded = assets + .ids() + .map(|id| server.load_state(id)) + .all(|load_state| { + !load_state.is_loading() && !load_state.is_failed() + }); + + progress.0.push(all_loaded); +} + +/// At the end of the frame check if all asset classes are loaded +fn check_progress( + progress: Res, + mut next_state: ResMut>, +) { + if progress.0.iter().all(|x| *x) { + next_state.set(GameState::Main); + } +}