Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor collide code (Adopted) #11106

Merged
merged 1 commit into from
Jan 1, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 123 additions & 18 deletions crates/bevy_sprite/src/collide_aabb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<Collision> {
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)
};
Expand All @@ -63,9 +79,98 @@ pub fn collide(a_pos: Vec3, a_size: Vec2, b_pos: Vec3, b_size: Vec2) -> Option<C
}

#[cfg(test)]
mod test {
mod tests {
use super::*;

///
///
/// ______
/// | |
/// | A |
/// ----|----|----
/// | |____| |
/// | B |
/// |____________|
///
#[test]
fn top_collision() {
let a = Vec3::new(0., 30., 0.);
let b = Vec3::new(0., 0., 0.);

check(a, b, Some(Collision::Top));
}

///
///
/// --------------
/// | B |
/// | _____ |
/// |___|____|___|
/// | |
/// | A |
/// | |
/// -----
#[test]
fn bottom_collision() {
let a = Vec3::new(0., -30., 0.);
let b = Vec3::new(0., 0., 0.);

check(a, b, Some(Collision::Bottom));
}

///
/// ______
/// | --|-----------
/// | | | |
/// | A | | B |
/// | |_|__________|
/// |_____|
///
#[test]
fn left_collision() {
let a = Vec3::new(0., 0., 0.);
let b = Vec3::new(30., 0., 0.);

check(a, b, Some(Collision::Left));
}

///
/// ______
/// -----------|-- |
/// | B | | |
/// | | | A |
/// |__________|_| |
/// |_____|
#[test]
fn right_collision() {
let a = Vec3::new(0., 0., 0.);
let b = Vec3::new(-30., 0., 0.);

check(a, b, Some(Collision::Right));
}

///
/// ______
/// ----|----|----
/// | | | |
/// | | | B |
/// |___|____|___|
/// | A |
/// |____|
#[test]
fn without_corners_on_intersection_area() {
let a = Vec3::new(0., 0., 0.);
let b = Vec3::new(0., 0., 0.);

check(a, b, Some(Collision::Inside));
}

fn check(a: Vec3, b: Vec3, expected: Option<Collision>) {
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),
Expand Down