From 23c14642b1d93905ab9bc86ca821183dd4a97f8c Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sun, 2 Jun 2019 11:34:51 +0200 Subject: Rework collision detection to use projection --- game_server/src/collide.rs | 113 ++++++++++++++++++++++++++++++--------------- game_server/src/maths.rs | 86 ++++++++++++++++++++++++++-------- 2 files changed, 144 insertions(+), 55 deletions(-) diff --git a/game_server/src/collide.rs b/game_server/src/collide.rs index 7439101..d510a04 100644 --- a/game_server/src/collide.rs +++ b/game_server/src/collide.rs @@ -18,54 +18,31 @@ impl Collide for AABox { impl Collide for AABox { fn collides(&self, other: &Self) -> bool { - self.collides(&other.pos) - || self.collides(&(other.pos + Vec2{x: other.size.x, y: 0.0})) - || self.collides(&(other.pos + Vec2{x: 0.0, y: other.size.y})) - || self.collides(&(other.pos + other.size)) - - || other.collides(&(self.pos)) - || other.collides(&(self.pos + Vec2{x: self.size.x, y: 0.0})) - || other.collides(&(self.pos + Vec2{x: 0.0, y: self.size.y})) - || other.collides(&(self.pos + self.size)) + self.pos.x < other.pos.x + other.size.x && other.pos.x < self.pos.x + self.size.x + && self.pos.y < other.pos.y + other.size.y && other.pos.y < self.pos.y + self.size.y } } impl Collide for RBox { fn collides(&self, other: &Vec2) -> bool { + let v1_diff = *other + self.v1 * (-self.v1.scalar(&other) / self.v1.distance2()); + let v2_diff = *other + self.v2 * (-self.v2.scalar(&other) / self.v2.distance2()); - let da = self.size.norm(); - let dax = other.x - self.pos.x; - let day = other.y - self.pos.y; - - let dot = dax * da.x + day * da.y; - let px = self.pos.x + da.x * dot; - let py = self.pos.y + da.y * dot; - - let p = Vec2{x: px, y: py}; - if !(self.pos < p && p < self.pos + self.size) { - return false; - } - - let ddx = other.x-px; - let ddy = other.y-py; - let manhattenDistance = ddx + ddy; - - manhattenDistance < self.w + self.pos < v1_diff && v1_diff < self.pos + self.v2 + && self.pos < v2_diff && v2_diff < self.pos + self.v1 } } impl Collide for RBox { fn collides(&self, other: &AABox) -> bool { - self.collides(&other.pos) - || self.collides(&(other.pos + Vec2{x: other.size.x, y: 0.0})) - || self.collides(&(other.pos + Vec2{x: 0.0, y: other.size.y})) - || self.collides(&(other.pos + other.size)) - - || other.collides(&(self.pos)) - || other.collides(&(self.pos + Vec2{x: self.size.x, y: 0.0})) - || other.collides(&(self.pos + Vec2{x: 0.0, y: self.size.y})) - || other.collides(&(self.pos + self.size)) - + let v1_diff = other.pos + self.v1 * (-self.v1.scalar(&other.pos) / self.v1.distance2()); + let v2_diff = other.pos + self.v2 * (-self.v2.scalar(&other.pos) / self.v2.distance2()); + let other_size = other.pos + other.size; + let v1_diff_size = other_size + self.v1 * (-self.v1.scalar(&other_size) / self.v1.distance2()); + let v2_diff_size = other_size + self.v2 * (-self.v2.scalar(&other_size) / self.v2.distance2()); + + self.pos < v1_diff + other.size && v1_diff < self.pos + self.v2 + && self.pos < v2_diff + other.size && v2_diff < self.pos + self.v1 } } @@ -74,3 +51,65 @@ impl> Collide for Vec { self.iter().any(|x| x.collides(other)) } } + +#[cfg(test)] + mod tests { + use super::*; + + #[test] + fn test_collide_dot_dot() { + let a = Vec2{x: 1.0, y: 7.5}; + assert!(a.collides(&a)); + } + + #[test] + fn test_not_collide_dot_dot() { + let a = Vec2{x: 1.0, y: 7.5}; + let b = Vec2{x: 5.0, y: 7.5}; + assert!(!a.collides(&b)); + } + + #[test] + fn test_collide_aabox_dot() { + let a = Vec2{x: 1.0, y: 2.5}; + let b = Vec2{x: 3.0, y: 7.5}; + let c = Vec2{x: 1.5, y: 5.0}; + let aa_box = AABox{pos: a, size: b}; + + assert!(aa_box.collides(&c)); + } + + #[test] + fn test_not_collide_aabox_aabox() { + let a = Vec2{x: 1.0, y: 7.5}; + let b = Vec2{x: 3.0, y: 2.5}; + let c = Vec2{x: 0.5, y: 5.0}; + let aa_box = AABox{pos: a, size: b}; + let a = Vec2{x: 1.0, y: 7.5}; + let b = Vec2{x: 3.0, y: 2.5}; + let c = Vec2{x: 0.5, y: 5.0}; + let aa_box = AABox{pos: a, size: b}; + + assert!(!(aa_box.collides(&c))); + } + + #[test] + fn test_collide_Rbox_dot() { + let a = Vec2{x: 1.0, y: 2.5}; + let b = Vec2{x: 3.0, y: 7.5}; + let c = Vec2{x: 1.5, y: 5.0}; + let aa_box = AABox{pos: a, size: b}; + + assert!(aa_box.collides(&c)); + } + + #[test] + fn test_not_collide_Rbox_dot() { + let a = Vec2{x: 1.0, y: 7.5}; + let b = Vec2{x: 3.0, y: 2.5}; + let c = Vec2{x: 0.5, y: 5.0}; + let aa_box = AABox{pos: a, size: b}; + + assert!(!(aa_box.collides(&c))); + } +} diff --git a/game_server/src/maths.rs b/game_server/src/maths.rs index 66c5bca..cc6c777 100644 --- a/game_server/src/maths.rs +++ b/game_server/src/maths.rs @@ -48,9 +48,19 @@ impl std::ops::Neg for Vec2 { } } +impl std::ops::Mul for Vec2 { + type Output = Self; + fn mul(self, scale: f32) -> Self { + Self { + x: self.x * scale, + y: -self.y * scale + } + } +} + impl std::cmp::PartialOrd for Vec2 { fn partial_cmp(&self, other: &Self) -> Option { - if self.x < other.x && self.y < other.y { + if self.x <= other.x && self.y <= other.y { Some(std::cmp::Ordering::Less) } else if self.x > other.x && self.y > other.y { Some(std::cmp::Ordering::Greater) @@ -75,7 +85,11 @@ impl Vec2 { } pub fn distance2(&self) -> f32 { - self.x * self.x + self.y * self.y + self.scalar(self) + } + + pub fn scalar(&self, other: &Vec2) -> f32 { + self.x * other.x + self.y * other.y } pub fn norm(&self) -> Vec2 { @@ -137,12 +151,12 @@ impl std::cmp::Eq for AABox {} #[derive(Clone, Copy, Debug)] pub struct RBox { - /// Point 1 + /// origin pub pos: Vec2, - /// Point 2 - pub size: Vec2, - /// Width Attention manhatten distance!!! - pub w: f32, + /// Vwctor1 + pub v1: Vec2, + /// Vector2 + pub v2: Vec2, } impl std::ops::Add for RBox { @@ -150,8 +164,8 @@ impl std::ops::Add for RBox { fn add(self, other: Vec2) -> Self { Self { pos: self.pos + other, - size: self.size, - w: self.w, + v1: self.v1, + v2: self.v2, } } } @@ -167,8 +181,8 @@ impl std::ops::Sub for RBox { fn sub(self, other: Vec2) -> Self { Self { pos: self.pos + other, - size: self.size + other, - w: self.w, + v1: self.v1 + other, + v2: self.v2, } } } @@ -182,8 +196,8 @@ impl std::ops::SubAssign for RBox { impl std::cmp::PartialEq for RBox { fn eq(&self, other: &Self) -> bool { self.pos == other.pos - && self.size == other.size - && f32::abs(self.w - other.w) < 1e-8 + && self.v1 == other.v1 + && self.v1 == self.v2 } } @@ -194,6 +208,39 @@ mod tests { // Note this useful idiom: importing names from outer (for mod tests) scope. use super::*; + #[test] + fn test_less_vec2() { + let a = Vec2{x: 1.0, y: 7.5}; + let b = Vec2{x: -3.0, y: 2.5}; + + assert!(b < a); + } + + #[test] + fn test_less_vec2_fail() { + let a = Vec2{x: 1.0, y: 7.5}; + let b = Vec2{x: 3.0, y: 2.5}; + + assert!(!(a < b)); + } + + #[test] + fn test_greater_vec2() { + let a = Vec2{x: 1.0, y: 7.5}; + let b = Vec2{x: -3.0, y: 2.5}; + + assert!(a > b); + } + + #[test] + fn test_greater_vec2_fail() { + let a = Vec2{x: 1.0, y: 7.5}; + let b = Vec2{x: 3.0, y: 2.5}; + + assert!(!(a > b)); + } + + #[test] fn test_add_vec2() { let a = Vec2{x: 1.0, y: 7.5}; @@ -233,6 +280,7 @@ mod tests { assert!(f32::abs(a.distance2() - 5.0) < 1e8); } + #[test] fn test_norm_vec2() { let a = Vec2{x: 2.0, y: -2.0}; @@ -267,8 +315,9 @@ mod tests { fn test_add_rbox_vec2() { let a = Vec2{x: 1.0, y: 7.5}; let b = Vec2{x: -3.0, y: 2.5}; - let mut aa_box = RBox{pos: a, size: b, w: 8.0}; - let bb_box = RBox{pos: a + b,size: b, w: 8.0}; + let c = Vec2{x: -3.0, y: 2.5}; + let mut aa_box = RBox{pos: a, v1: b, v2: c}; + let bb_box = RBox{pos: a + b, v1: b, v2: c}; aa_box += b; assert_eq!(aa_box, bb_box); @@ -278,10 +327,11 @@ mod tests { fn test_sub_rbox_vec2() { let a = Vec2{x: 1.0, y: 7.5}; let b = Vec2{x: -3.0, y: 2.5}; - let mut aa_box = RBox{pos: a, size: b, w: 8.0}; - let bb_box = RBox{pos: a - b,size: b, w: 8.0}; + let c = Vec2{x: -3.0, y: 2.5}; + let mut aa_box = RBox{pos: a, v1: b, v2: c}; + let bb_box = RBox{pos: a - b, v1: b, v2: c}; aa_box -= b; assert_eq!(aa_box, bb_box); } -} \ No newline at end of file +} -- cgit v1.2.3-54-g00ecf