Skip to content

Commit

Permalink
Factor the physics code into a seperate module
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-thompson committed Jan 23, 2024
1 parent a7bc050 commit 2d83c35
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 200 deletions.
4 changes: 2 additions & 2 deletions src/dashboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#![allow(clippy::type_complexity)]

use super::{Player, Velocity};
use super::{physics, Player};
use bevy::prelude::*;

#[derive(Component, Debug)]
Expand Down Expand Up @@ -91,7 +91,7 @@ fn spawn_dashboard(mut commands: Commands, asset_server: Res<AssetServer>) {
}

fn update_speedo(
player: Query<(&Velocity, With<Player>)>,
player: Query<(&physics::Velocity, With<Player>)>,
mut speedo: Query<(&mut Transform, With<Speedometer>)>,
) {
let (vp, _) = player.single();
Expand Down
218 changes: 20 additions & 198 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,19 @@

#![allow(clippy::type_complexity)]

use bevy::{
math::{vec2, vec3},
prelude::*,
render::camera::ScalingMode,
window,
};
use bevy::{math::vec3, prelude::*, render::camera::ScalingMode, window};
use bevy_ecs_tilemap::prelude as ecs_tilemap;
use clap::Parser;
use slicetools::*;
use std::f32::consts::PI;

mod assets;
mod dashboard;
mod editor;
mod mapping;
mod physics;
mod tilemap;
mod util;

use util::IteratorToArrayExt;

#[derive(Clone, Debug, Parser, Resource)]
#[command(author, version, about, long_about = None)]
struct Preferences {
Expand Down Expand Up @@ -90,13 +83,13 @@ fn main() {
(
handle_keyboard,
handle_ai_players,
apply_velocity
physics::apply_velocity
.after(handle_ai_players)
.after(handle_keyboard),
apply_friction.after(apply_velocity),
track_player.after(apply_velocity),
collision_detection
.after(apply_velocity)
physics::apply_friction.after(physics::apply_velocity),
track_player.after(physics::apply_velocity),
physics::collision_detection
.after(physics::apply_velocity)
.after(handle_keyboard)
.after(handle_ai_players),
),
Expand All @@ -116,27 +109,6 @@ struct Racer {
#[derive(Component, Default, Debug)]
struct Track;

#[derive(Component, Debug, Reflect)]
struct Velocity(Vec2);

#[derive(Component, Clone, Debug, Reflect)]
struct Angle(f32);

impl Angle {
fn normalize(&mut self) {
while self.0 > PI {
self.0 -= 2.0 * PI;
}
while self.0 < -PI {
self.0 += 2.0 * PI;
}
}

fn to_quat(&self) -> Quat {
Quat::from_rotation_z(self.0 - PI / 2.0)
}
}

fn spawn_camera(mut commands: Commands) {
let mut camera = Camera2dBundle::default();
// Request a constant width projection. 24 is the width in world units.
Expand Down Expand Up @@ -171,8 +143,8 @@ fn spawn_player(
commands.spawn((
Player,
Racer::default(),
Angle(0.0),
Velocity(Vec2::new(0.0, 20.0)),
physics::Angle(0.0),
physics::Velocity(Vec2::new(0.0, 20.0)),
SpriteSheetBundle {
texture_atlas: texture_atlas.add(atlas),
transform: Transform {
Expand All @@ -196,8 +168,8 @@ fn spawn_ai_players(

commands.spawn((
Racer::default(),
Angle(PI / 12.0),
Velocity(Vec2::new(0.0, 20.0)),
physics::Angle(PI / 12.0),
physics::Velocity(Vec2::new(0.0, 20.0)),
SpriteSheetBundle {
texture_atlas: texture_atlas.add(atlas),
transform: Transform {
Expand All @@ -214,8 +186,8 @@ fn spawn_ai_players(
let atlas = TextureAtlas::from_grid(handle, Vec2::new(70., 121.), 1, 1, None, None);
commands.spawn((
Racer::default(),
Angle(PI / 12.0),
Velocity(Vec2::new(0.0, 20.0)),
physics::Angle(PI / 12.0),
physics::Velocity(Vec2::new(0.0, 20.0)),
SpriteSheetBundle {
texture_atlas: texture_atlas.add(atlas),
transform: Transform {
Expand All @@ -232,8 +204,8 @@ fn spawn_ai_players(
let atlas = TextureAtlas::from_grid(handle, Vec2::new(70., 121.), 1, 1, None, None);
commands.spawn((
Racer::default(),
Angle(PI / 12.0),
Velocity(Vec2::new(0.0, 20.0)),
physics::Angle(PI / 12.0),
physics::Velocity(Vec2::new(0.0, 20.0)),
SpriteSheetBundle {
texture_atlas: texture_atlas.add(atlas),
transform: Transform {
Expand All @@ -246,160 +218,10 @@ fn spawn_ai_players(
));
}

fn apply_friction(
mut query: Query<(&mut Velocity, &mut Transform)>,
time: Res<Time>,
guide: Option<Res<mapping::GuidanceField>>,
) {
let delta = time.delta_seconds();
for (mut v, t) in query.iter_mut() {
v.0 *= 1.0 - (delta * 1.2);

if let Some(guide) = &guide {
let pos = Vec2::new(t.translation.x, t.translation.y);
let pixel = guide.get(&pos);
if pixel < 140 {
let factor = 1.2 + 1.2 * (1.0 - (pixel as f32 / 140.0));
v.0 *= 1.0 - (delta * factor);
}
}
}
}

fn apply_velocity(mut query: Query<(&Velocity, &mut Transform)>, time: Res<Time>) {
let delta = time.delta_seconds();
for (v, mut t) in query.iter_mut() {
t.translation.x += delta * v.0.x;
t.translation.y += delta * v.0.y;
}
}

fn same_side(p1: Vec2, p2: Vec2, line: (Vec2, Vec2)) -> bool {
let p1 = Vec3::from((p1, 0.0));
let p2 = Vec3::from((p2, 0.0));
let line = (Vec3::from((line.0, 0.0)), Vec3::from((line.1, 0.0)));

let cp1 = (line.1 - line.0).cross(p1 - line.0);
let cp2 = (line.1 - line.0).cross(p2 - line.0);

cp1.dot(cp2) >= 0.0
}

fn point_in_polygon(pt: Vec2, shape: &[Vec2]) -> bool {
let n = shape.len();
shape
.windows(3)
.chain(std::iter::once(
[shape[n - 2], shape[n - 1], shape[0]].as_slice(),
))
.chain(std::iter::once(
[shape[n - 1], shape[0], shape[1]].as_slice(),
))
.all(|x| same_side(pt, x[0], (x[1], x[2])))
}

struct CollisionBox {
points: [Vec2; 8],
}

impl CollisionBox {
fn from_transform(tf: &Transform, sz: &Vec2) -> Self {
let w = sz.x * 0.5;
let h = sz.y * 0.5;

// c is used to round the corners of the box, choosing
// 2.5 is a little arbitrary but it gives a good "feel"
// for most artwork... and you could handle special cases
// by creating the box by hand.
let c = w.min(h) / 2.5;

Self {
points: [
vec2(c - w, h),
vec2(w - c, h),
vec2(w, h - c),
vec2(w, c - h),
vec2(w - c, -h),
vec2(c - w, -h),
vec2(-w, c - h),
vec2(-w, h - c),
]
.iter()
.map(|v2| {
let v3 = Vec3::from((*v2, 0.0));
let pt = tf.transform_point(v3);
vec2(pt.x, pt.y)
})
.to_array(),
}
}

/// Test whether two rectangles are touching.
fn is_touching(&self, other: &CollisionBox) -> bool {
other
.points
.iter()
.any(|pt| point_in_polygon(*pt, &self.points))
|| self
.points
.iter()
.any(|pt| point_in_polygon(*pt, &other.points))
}

fn draw(&self, gizmos: &mut Gizmos) {
for w in self.points.windows(2) {
gizmos.line_2d(w[0], w[1], Color::BLUE);
}
gizmos.line_2d(self.points[7], self.points[0], Color::BLUE);
}
}

fn collision_detection(
mut query: Query<(&mut Transform, &Handle<TextureAtlas>, &mut Velocity)>,
texture_atlases: Res<Assets<TextureAtlas>>,
prefs: Res<Preferences>,
mut gizmos: Gizmos,
) {
let mut colliders = query.iter_mut().collect::<Vec<_>>();
let mut it = colliders.pairs_mut();
while let Some((a, b)) = it.next() {
let atx = match texture_atlases.get(a.1) {
Some(tx) => tx,
None => continue,
};
let btx = match texture_atlases.get(b.1) {
Some(tx) => tx,
None => continue,
};

let mut abox = CollisionBox::from_transform(&a.0, &atx.size);
let mut bbox = CollisionBox::from_transform(&b.0, &btx.size);
if prefs.debug_low() {
abox.draw(&mut gizmos);
bbox.draw(&mut gizmos);
}

if abox.is_touching(&bbox) {
std::mem::swap(&mut a.2 .0, &mut b.2 .0);

let a2 = vec2(a.0.translation.x, a.0.translation.y);
let b2 = vec2(b.0.translation.x, b.0.translation.y);
let nudge = Vec3::from(((b2 - a2).normalize() * 0.5, 0.0));
while abox.is_touching(&bbox) {
a.0.translation -= nudge;
b.0.translation += nudge;

abox = CollisionBox::from_transform(&a.0, &atx.size);
bbox = CollisionBox::from_transform(&b.0, &btx.size);
}
}
}
}

fn handle_keyboard(
mut query: Query<(
&mut Angle,
&mut Velocity,
&mut physics::Angle,
&mut physics::Velocity,
&mut Transform,
&mut Racer,
With<Player>,
Expand Down Expand Up @@ -436,8 +258,8 @@ fn handle_keyboard(

fn handle_ai_players(
mut query: Query<(
&mut Angle,
&mut Velocity,
&mut physics::Angle,
&mut physics::Velocity,
&mut Transform,
&mut Racer,
Without<Player>,
Expand Down Expand Up @@ -508,7 +330,7 @@ fn handle_ai_players(
}

fn track_player(
player: Query<(&Transform, &Velocity, With<Player>)>,
player: Query<(&Transform, &physics::Velocity, With<Player>)>,
mut camera: Query<(&mut Transform, With<Camera>, Without<Player>)>,
) {
let (txp, _, _) = player.single();
Expand Down
Loading

0 comments on commit 2d83c35

Please sign in to comment.