Skip to content
Snippets Groups Projects
physics.rs 2.51 KiB
Newer Older
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),
		);
	}
}