From c48066bc5b77547a1f9dc139fa45f1a3fba82e8d 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 bug fix + refactor. Closes #4478 --- crates/bevy_sprite/src/collide_aabb.rs | 146 ++++++++++++++++++++++--- 1 file changed, 128 insertions(+), 18 deletions(-) diff --git a/crates/bevy_sprite/src/collide_aabb.rs b/crates/bevy_sprite/src/collide_aabb.rs index 05a4e9fe2af1d..9a811166d8f98 100644 --- a/crates/bevy_sprite/src/collide_aabb.rs +++ b/crates/bevy_sprite/src/collide_aabb.rs @@ -2,7 +2,7 @@ use bevy_math::{Vec2, Vec3}; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum Collision { Left, Right, @@ -11,36 +11,52 @@ 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` component /// * `a_size` and `b_size` are the dimensions (width and height) of the rectangles. 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) }; @@ -55,3 +71,97 @@ 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); + } +}