diff --git a/src/cuwusions.rs b/src/cuwusions.rs new file mode 100644 index 0000000..56daa62 --- /dev/null +++ b/src/cuwusions.rs @@ -0,0 +1,76 @@ +// assume collision resolution takes place before velocity resolution + +pub fn rects_collision_resolution_pos( + pos: (f64, f64), + vel: (f64, f64), + rect: (f64, f64), + other_pos: (f64, f64), + other_rect: (f64, f64), +) -> (f64, f64) { + 'top_left: { + let Some((intersection, dist)) = + closest_point_rect_intersection(pos, vel, other_pos, other_rect) + else { + let (x, y) = pos; + break 'top_left (x + vel.0, y + vel.1); + }; + + intersection + } +} + +fn point_distance(a: (f64, f64), b: (f64, f64)) -> f64 { + ((a.0 - b.0).abs().powi(2) + (a.1 - b.1).abs().powi(2)).sqrt() +} + +fn horizontal_line_intersect(p0: (f64, f64), vel: (f64, f64), line_y: f64) -> Option<(f64, f64)> { + if vel.1 == 0.0 { + // exclusively going left or right, ergo there's no collision with a horizontal line + return None; + } else if vel.0 == 0.0 { + // no change in x, ergo the intersect must be at p0_x + return Some((p0.0, line_y)); + } + // y = ax + b + let a = vel.1 / vel.0; + let b = p0.1 - p0.0 * a; + + let x = (line_y - b) / a; + Some((x, line_y)) +} + +fn vertical_line_intersect(p0: (f64, f64), vel: (f64, f64), line_x: f64) -> Option<(f64, f64)> { + if vel.0 == 0.0 { + // exclusively going up or down, ergo there's no collision with a vertical line + return None; + } else if vel.1 == 0.0 { + // no change in y, ergo the intersect must be at p0_y + return Some((line_x, p0.1)); + } + // + // y = ax + b + let a = vel.1 / vel.0; + let b = p0.1 - p0.0 * a; + + let y = a * line_x + b; + Some((line_x, y)) +} + +fn closest_point_rect_intersection( + p0: (f64, f64), + vel: (f64, f64), + pos: (f64, f64), + rect: (f64, f64), +) -> Option<((f64, f64), f64)> { + [ + horizontal_line_intersect(p0, vel, pos.1), + horizontal_line_intersect(p0, vel, pos.1 + rect.1), + vertical_line_intersect(p0, vel, pos.0 + rect.0), + vertical_line_intersect(p0, vel, pos.0), + ] + .into_iter() + .flatten() + .map(|point| (point, point_distance(p0, point))) + .filter(|(_, dist)| *dist <= point_distance((0.0, 0.0), vel)) + .min_by(|(_, dist_a), (_, dist_b)| dist_a.total_cmp(dist_b)) +} diff --git a/src/main.rs b/src/main.rs index 7c00f6d..1904782 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![allow(dead_code)] +mod cuwusions; mod engine; use engine::{Component, System}; @@ -9,7 +10,7 @@ struct Sprite { sprite: engine::Sprite, } -#[derive(Component, Default, Clone)] +#[derive(Component, Default, Clone, Debug)] struct RigidBody { pos: (f64, f64), vel: (f64, f64), @@ -22,8 +23,8 @@ impl System for VelocitySystem { fn on_update(&self, ctx: &mut engine::Context, delta: f64) -> Result<(), engine::Error> { for id in query!(ctx, RigidBody) { let body = ctx.entity_component::(id); - body.pos.0 += body.vel.0 * delta; - body.pos.1 += body.vel.1 * delta; + // body.pos.0 += body.vel.0 * delta; + // body.pos.1 += body.vel.1 * delta; } Ok(()) } @@ -154,51 +155,16 @@ impl System for CollisionSystem { continue; } let other_body = ctx.entity_component::(other_id).clone(); - if rects_collide(body.rect, body.pos, other_body.rect, other_body.pos) { - let last_pos = ( - body.pos.0 - body.vel.0 * delta, - body.pos.1 - body.vel.1 * delta, - ); - if rects_collide(body.rect, last_pos, other_body.rect, other_body.pos) { - panic!() - } - let closest_surface = [ - (last_pos.0, last_pos.1), - (last_pos.0, last_pos.1 + body.rect.1), - (last_pos.0 + body.rect.0, last_pos.1), - (last_pos.0 + body.rect.0, last_pos.1 + body.rect.1), - ] - .into_iter() - .filter_map(|p0| { - point_rect_closest_surface(p0, body.vel, other_body.pos, other_body.rect) - }) - .min_by(|(dist_a, _), (dist_b, _)| dist_a.total_cmp(dist_b)) - .map(|(_, surface)| surface) - .ok_or("we already checked if collision happens")?; + let new_pos = cuwusions::rects_collision_resolution_pos( + body.pos, + (body.vel.0 * delta, body.vel.1 * delta), + body.rect, + other_body.pos, + other_body.rect, + ); - let collider = ctx.entity_component::(id); - collider.colliding_surface = Some(closest_surface.clone()); - let body = ctx.entity_component::(id); - println!("closest: {closest_surface:?} | vel: {:?}", body.vel); - match closest_surface { - Surface::Top => { - body.vel.1 = 0.0; - body.pos.1 = other_body.pos.1 - body.rect.1; - } - Surface::Bottom => { - body.vel.1 = 0.0; - body.pos.1 = other_body.pos.1 + other_body.rect.1; - } - Surface::Left => { - body.vel.0 = 0.0; - body.pos.0 = other_body.pos.0 - body.rect.0; - } - Surface::Right => { - body.vel.0 = 0.0; - body.pos.0 = other_body.pos.0 + other_body.rect.0; - } - } - } + let body = ctx.entity_component::(id); + body.pos = new_pos; } } Ok(())