From b7575f113a4ddc17c6789eb5f45e55d57fd160b2 Mon Sep 17 00:00:00 2001 From: Louis Capitanchik <contact@louiscap.co> Date: Sun, 23 Jul 2023 01:20:26 +0100 Subject: [PATCH] Pull configs into hot-reload assets --- Cargo.lock | 1 + Cargo.toml | 1 + assets/config/config.phys | 5 ++ game_core/Cargo.toml | 3 +- game_core/src/assets/configs.rs | 81 +++++++++++++++++++++++++++++++ game_core/src/assets/mod.rs | 2 + game_core/src/debug/mod.rs | 11 +++-- game_core/src/entities/physics.rs | 22 ++++++++- game_core/src/entities/player.rs | 16 ++++-- game_core/src/main.rs | 3 +- game_core/src/system/resources.rs | 2 +- 11 files changed, 133 insertions(+), 14 deletions(-) create mode 100644 assets/config/config.phys create mode 100644 game_core/src/assets/configs.rs diff --git a/Cargo.lock b/Cargo.lock index 5c5bf6d..c327bb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1639,6 +1639,7 @@ dependencies = [ name = "game_core" version = "0.1.0" dependencies = [ + "anyhow", "bevy", "bevy_embedded_assets", "bevy_rapier2d", diff --git a/Cargo.toml b/Cargo.toml index a9df19c..38dbbc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ serde = "1.0.164" serde_json = "1.0.96" num-traits = "0.2.15" paste = "1.0.12" +anyhow = "1.0.71" bevy_rapier2d = { version = "0.22.0", features = ["simd-stable", "wasm-bindgen"]} bevy_embedded_assets = "0.8.0" diff --git a/assets/config/config.phys b/assets/config/config.phys new file mode 100644 index 0000000..727ab38 --- /dev/null +++ b/assets/config/config.phys @@ -0,0 +1,5 @@ +{ + "gravity": -123.0, + "jump": 200.0, + "speed": 150.0 +} \ No newline at end of file diff --git a/game_core/Cargo.toml b/game_core/Cargo.toml index 5d89f4f..9b857ad 100644 --- a/game_core/Cargo.toml +++ b/game_core/Cargo.toml @@ -21,4 +21,5 @@ bevy_rapier2d.workspace = true serde.workspace = true serde_json.workspace = true num-traits.workspace = true -paste.workspace = true \ No newline at end of file +paste.workspace = true +anyhow.workspace = true \ No newline at end of file diff --git a/game_core/src/assets/configs.rs b/game_core/src/assets/configs.rs new file mode 100644 index 0000000..bcbb54e --- /dev/null +++ b/game_core/src/assets/configs.rs @@ -0,0 +1,81 @@ +use crate::system::AppState; +use bevy::asset::{AddAsset, AssetLoader, AssetServer, BoxedFuture, LoadContext, LoadedAsset}; +use bevy::ecs::system::SystemParam; +use bevy::prelude::*; +use bevy::reflect::{TypePath, TypeUuid}; +use paste::paste; +use serde::{Deserialize, Serialize}; + +macro_rules! impl_config_lifecycle { + ($(($struct: ident, $ext: literal)),*) => { + paste! { + #[allow(dead_code)] + mod _inner { + use super::*; + $( + struct [<$struct Loader>]; + impl AssetLoader for [<$struct Loader>] { + fn load<'a>( + &'a self, + bytes: &'a [u8], + load_context: &'a mut LoadContext, + ) -> BoxedFuture<'a, anyhow::Result<()>> { + Box::pin(async move { + let value: $struct = serde_json::from_slice(bytes)?; + load_context.set_default_asset(LoadedAsset::new(value)); + Ok(()) + }) + } + + fn extensions(&self) -> &[&str] { + &[$ext] + } + } + #[derive(Resource)] + struct [<$struct Wrapper>] { + handle: Handle<$struct>, + } + + #[derive(SystemParam)] + pub struct [<Fetch $struct>]<'w> { + wrapper: Res<'w, [<$struct Wrapper>]>, + asset: Res<'w, Assets<$struct>>, + } + + impl<'w> [<Fetch $struct>]<'w> { + pub fn get(&self) -> Option<&$struct> { + self.asset.get(&self.wrapper.handle) + } + } + + fn [<init_ $struct:snake>](mut commands: Commands, assets: Res<AssetServer>) { + let handle = assets.load(format!("config/config.{}", $ext)); + commands.insert_resource([<$struct Wrapper>] { handle }); + } + )* + + pub struct ConfigsPlugin; + impl Plugin for ConfigsPlugin { + fn build(&self, app: &mut App) {$( + app.add_asset::<$struct>() + .add_asset_loader([<$struct Loader>]) + .add_systems(OnEnter(AppState::Preload), [<init_ $struct:snake>]); + )*} + } + } + + pub use _inner::ConfigsPlugin; + pub use _inner::{$([<Fetch $struct>]),*}; + } + }; +} + +#[derive(Copy, Clone, TypeUuid, TypePath, Debug, Default, Serialize, Deserialize)] +#[uuid = "f7d88160-28e4-11ee-bcad-b34d9ff949be"] +pub struct PhysicsConfig { + pub gravity: f32, + pub jump: f32, + pub speed: f32, +} + +impl_config_lifecycle!((PhysicsConfig, "phys")); diff --git a/game_core/src/assets/mod.rs b/game_core/src/assets/mod.rs index bfdb073..8edb7a0 100644 --- a/game_core/src/assets/mod.rs +++ b/game_core/src/assets/mod.rs @@ -1,3 +1,4 @@ +mod configs; mod loader; mod resources; mod startup; @@ -23,6 +24,7 @@ mod _plugin { } pub use _plugin::AssetLoadingPlugin; +pub use configs::{ConfigsPlugin, FetchPhysicsConfig, PhysicsConfig}; pub(self) use loader::AssetTypeLoader; pub use resources::AssetHandles; pub(self) use resources::{AssetNameMapping, FixedAssetNameMapping, SpriteSheetConfig}; diff --git a/game_core/src/debug/mod.rs b/game_core/src/debug/mod.rs index 1d663d5..84cc351 100644 --- a/game_core/src/debug/mod.rs +++ b/game_core/src/debug/mod.rs @@ -15,11 +15,12 @@ mod _plugin { Collider::cuboid(200.0, 50.0), )); }) - .add_systems( - Update, - (|gamepads: Res<Gamepads>| info!("{:?}", gamepads)) - .run_if(on_fixed_timer(Duration::from_secs(5))), - ); + // .add_systems( + // Update, + // (|gamepads: Res<Gamepads>| info!("{:?}", gamepads)) + // .run_if(on_fixed_timer(Duration::from_secs(5))), + // ) + ; } } } diff --git a/game_core/src/entities/physics.rs b/game_core/src/entities/physics.rs index f97b936..57392d7 100644 --- a/game_core/src/entities/physics.rs +++ b/game_core/src/entities/physics.rs @@ -1,4 +1,6 @@ +use crate::assets::FetchPhysicsConfig; use crate::system::run_in_game; +use bevy::ecs::query::Has; use bevy::prelude::*; use bevy_rapier2d::prelude::KinematicCharacterController; @@ -8,6 +10,9 @@ pub const GRAVITY_VEC: Vec2 = Vec2::new(0.0, GRAVITY_ACC); #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, SystemSet)] pub struct PhysicsSet; +#[derive(Component, Clone, Copy, Debug)] +pub struct HasGravity; + #[derive(Component, Clone, Copy, Debug, Default, Deref, DerefMut)] pub struct Velocity(Vec2); @@ -43,15 +48,28 @@ impl Acceleration { pub fn gravity() -> Self { Acceleration(GRAVITY_VEC) } + pub fn new(val: Vec2) -> Self { + Acceleration(val) + } } fn apply_acceleration( time: Res<Time>, - mut query: Query<(&mut Velocity, &Acceleration, Option<&PhysicsLimits>)>, + mut query: Query<( + &mut Velocity, + &Acceleration, + Option<&PhysicsLimits>, + Has<HasGravity>, + )>, + phys_config: FetchPhysicsConfig, ) { + let gravity = phys_config.get().map(|p| p.gravity).unwrap_or_default(); let dt = time.delta_seconds(); - for (mut velocity, acceleration, limits) in &mut query { + for (mut velocity, acceleration, limits, has_gravity) in &mut query { **velocity += **acceleration * dt; + if has_gravity { + **velocity += Vec2::new(0.0, gravity) * dt; + } **velocity = limits .copied() .unwrap_or_default() diff --git a/game_core/src/entities/player.rs b/game_core/src/entities/player.rs index c6a6fda..5da2d04 100644 --- a/game_core/src/entities/player.rs +++ b/game_core/src/entities/player.rs @@ -1,7 +1,8 @@ -use crate::assets::AssetHandles; +use crate::assets::{AssetHandles, FetchPhysicsConfig}; use crate::system::{AppState, ChaseCam, PolyInputManager}; use bevy::prelude::*; +use crate::entities::physics::HasGravity; use crate::entities::{ Acceleration, PhysicsLimits, PhysicsSet, SimpleAnimation, SimpleAnimationBundle, Velocity, GRAVITY_ACC, @@ -27,8 +28,9 @@ pub fn spawn_player(mut commands: Commands, assets: Res<AssetHandles>) { offset: CharacterLength::Absolute(0.03), ..Default::default() }, - Acceleration::gravity(), + Acceleration::default(), Velocity::default(), + HasGravity, PhysicsLimits { velocity: ( Some(Vec2::new(f32::NEG_INFINITY, GRAVITY_ACC)), @@ -53,7 +55,13 @@ pub fn spawn_player(mut commands: Commands, assets: Res<AssetHandles>) { pub fn handle_input( input: PolyInputManager, mut query: Query<(&mut Velocity, &KinematicCharacterControllerOutput), With<Player>>, + phys_config: FetchPhysicsConfig, ) { + let speed = phys_config + .get() + .map(|phys_config| phys_config.speed) + .unwrap_or_default(); + for (mut velocity, controller) in &mut query { let delta_y = if controller.grounded && input.is_jump_just_pressed() { Some(40.0) @@ -62,9 +70,9 @@ pub fn handle_input( }; let delta_x = if input.is_right_pressed_or_active() { - 200.0 + speed } else if input.is_left_pressed_or_active() { - -200.0 + -speed } else { 0.0 }; diff --git a/game_core/src/main.rs b/game_core/src/main.rs index 9eb1379..8f74f60 100644 --- a/game_core/src/main.rs +++ b/game_core/src/main.rs @@ -2,8 +2,9 @@ use bevy::prelude::App; fn main() { App::new() - .add_plugins(game_core::assets::AssetLoadingPlugin) .add_plugins(game_core::system::SystemPluginSet) + .add_plugins(game_core::assets::AssetLoadingPlugin) + .add_plugins(game_core::assets::ConfigsPlugin) .add_plugins(game_core::entities::EntityPluginSet) .add_plugins(game_core::debug::DebugPlugin) .add_plugins(bevy_rapier2d::plugin::RapierPhysicsPlugin::<()>::pixels_per_meter(18.0)) diff --git a/game_core/src/system/resources.rs b/game_core/src/system/resources.rs index 36c5482..f643b4f 100644 --- a/game_core/src/system/resources.rs +++ b/game_core/src/system/resources.rs @@ -31,7 +31,7 @@ pub fn configure_default_plugins() -> PluginGroupBuilder { }) .set(AssetPlugin { asset_folder: get_asset_path_string(), - watch_for_changes: ChangeWatcher::with_delay(Duration::from_secs(1)), + watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(25)), }) .set(ImagePlugin::default_nearest()) .set(LogPlugin { -- GitLab