From 11aefaa78f4d19629da3d79e24929322b4f782cc Mon Sep 17 00:00:00 2001 From: Eduardo Canellas de Oliveira Date: Fri, 15 Apr 2022 12:49:05 -0300 Subject: [PATCH] improve collide handling --- crates/bevy_sprite/src/collide_aabb.rs | 141 +++++++++++++++++++++---- 1 file changed, 123 insertions(+), 18 deletions(-) diff --git a/crates/bevy_sprite/src/collide_aabb.rs b/crates/bevy_sprite/src/collide_aabb.rs index 52d9fd1152e1e..cc9c7c6c4bf41 100644 --- a/crates/bevy_sprite/src/collide_aabb.rs +++ b/crates/bevy_sprite/src/collide_aabb.rs @@ -12,8 +12,29 @@ pub enum Collision { Inside, } +struct CollisionBox { + pub top: f32, + pub bottom: f32, + pub left: f32, + pub right: f32, +} + +impl CollisionBox { + pub fn new(pos: Vec3, size: Vec2) -> Self { + Self { + top: pos.y + size.y / 2., + bottom: pos.y - size.y / 2., + left: pos.x - size.x / 2., + right: pos.x + size.x / 2., + } + } +} + // TODO: ideally we can remove this once bevy gets a physics system -/// Axis-aligned bounding box collision with "side" detection +/// Axis-aligned bounding box collision with "side" detection. +/// +/// The [Collision], in case it occurred, is the side of `b` where `a` hit. +/// /// * `a_pos` and `b_pos` are the center positions of the rectangles, typically obtained by /// extracting the `translation` field from a [`Transform`](bevy_transform::components::Transform) component /// * `a_size` and `b_size` are the dimensions (width and height) of the rectangles. @@ -23,30 +44,25 @@ pub enum Collision { /// If the collision occurs on multiple sides, the side with the shallowest penetration is returned. /// If all sides are involved, [`Collision::Inside`] is returned. pub fn collide(a_pos: Vec3, a_size: Vec2, b_pos: Vec3, b_size: Vec2) -> Option { - let a_min = a_pos.truncate() - a_size / 2.0; - let a_max = a_pos.truncate() + a_size / 2.0; - - let b_min = b_pos.truncate() - b_size / 2.0; - let b_max = b_pos.truncate() + b_size / 2.0; + let a = CollisionBox::new(a_pos, a_size); + let b = CollisionBox::new(b_pos, b_size); // check to see if the two rectangles are intersecting - if a_min.x < b_max.x && a_max.x > b_min.x && a_min.y < b_max.y && a_max.y > b_min.y { + if a.left < b.right && a.right > b.left && a.bottom < b.top && a.top > b.bottom { // check to see if we hit on the left or right side - let (x_collision, x_depth) = if a_min.x < b_min.x && a_max.x > b_min.x && a_max.x < b_max.x - { - (Collision::Left, b_min.x - a_max.x) - } else if a_min.x > b_min.x && a_min.x < b_max.x && a_max.x > b_max.x { - (Collision::Right, a_min.x - b_max.x) + let (x_collision, x_depth) = if a.left < b.left && a.right > b.left && a.right < b.right { + (Collision::Left, b.left - a.right) + } else if a.left > b.left && a.left < b.right && a.right > b.right { + (Collision::Right, a.left - b.right) } else { (Collision::Inside, -f32::INFINITY) }; // check to see if we hit on the top or bottom side - let (y_collision, y_depth) = if a_min.y < b_min.y && a_max.y > b_min.y && a_max.y < b_max.y - { - (Collision::Bottom, b_min.y - a_max.y) - } else if a_min.y > b_min.y && a_min.y < b_max.y && a_max.y > b_max.y { - (Collision::Top, a_min.y - b_max.y) + let (y_collision, y_depth) = if a.bottom < b.bottom && a.top > b.bottom && a.top < b.top { + (Collision::Bottom, b.bottom - a.top) + } else if a.bottom > b.bottom && a.bottom < b.top && a.top > b.top { + (Collision::Top, a.bottom - b.top) } else { (Collision::Inside, -f32::INFINITY) }; @@ -63,9 +79,98 @@ pub fn collide(a_pos: Vec3, a_size: Vec2, b_pos: Vec3, b_size: Vec2) -> Option) { + let a_size = Vec2::new(30., 50.); + let b_size = Vec2::new(50., 30.); + assert_eq!(collide(a, a_size, b, b_size), expected); + } + fn collide_two_rectangles( // (x, y, size x, size y) a: (f32, f32, f32, f32),