use crate::assets::FetchPhysicsConfig; use crate::system::run_in_game; use bevy::ecs::query::Has; use bevy::prelude::*; use bevy_rapier2d::prelude::KinematicCharacterController; pub const GRAVITY_ACC: f32 = -150.0; 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); #[derive(Component, Clone, Copy, Debug, Default, Deref, DerefMut)] pub struct Acceleration(Vec2); #[derive(Component, Clone, Copy, Debug, Default)] pub struct PhysicsLimits { pub velocity: (Option<Vec2>, Option<Vec2>), pub acceleration: (Option<Vec2>, Option<Vec2>), } impl PhysicsLimits { pub fn limit_velocity(&self, velocity: Vec2) -> Vec2 { match self.velocity { (None, None) => velocity, (Some(min), None) => velocity.max(min), (None, Some(max)) => velocity.min(max), (Some(min), Some(max)) => velocity.min(max).max(min), } } pub fn limit_acceleration(&self, acceleration: Vec2) -> Vec2 { match self.acceleration { (None, None) => acceleration, (Some(min), None) => acceleration.max(min), (None, Some(max)) => acceleration.min(max), (Some(min), Some(max)) => acceleration.min(max).max(min), } } } 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>, 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, has_gravity) in &mut query { **velocity += **acceleration * dt; if has_gravity { **velocity += Vec2::new(0.0, gravity) * dt; } **velocity = limits .copied() .unwrap_or_default() .limit_velocity(**velocity); } } fn apply_velocity_to_kinematics( time: Res<Time>, mut query: Query<(&mut KinematicCharacterController, &Velocity)>, ) { let dt = time.delta_seconds(); for (mut controller, velocity) in &mut query { controller.translation = Some(**velocity * dt); } } pub struct PhysicsPlugin; impl Plugin for PhysicsPlugin { fn build(&self, app: &mut App) { app.add_systems( Update, (apply_acceleration, apply_velocity_to_kinematics) .chain() .in_set(PhysicsSet) .run_if(run_in_game), ); } }