Add more documentation and tests to collide_aabb::collide() (#5910)
While looking into `collide()`, I wrote some tests to confirm the behavior I read in the code. This PR adds those tests and improves the documentation. Co-authored-by: robem <669201+robem@users.noreply.github.com>
This commit is contained in:
parent
fc07557913
commit
301ecf65ba
@ -2,7 +2,7 @@
|
||||
|
||||
use bevy_math::{Vec2, Vec3};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Collision {
|
||||
Left,
|
||||
Right,
|
||||
@ -16,6 +16,11 @@ pub enum Collision {
|
||||
/// * `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.
|
||||
///
|
||||
/// The return value is the side of `B` that `A` has collided with. `Left` means that
|
||||
/// `A` collided with `B`'s left side. `Top` means that `A` collided with `B`'s top side.
|
||||
/// If the collision occurs on multiple sides, the side with the deepest penetration is returned.
|
||||
/// If all sides are involved, `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;
|
||||
@ -55,3 +60,132 @@ pub fn collide(a_pos: Vec3, a_size: Vec2, b_pos: Vec3, b_size: Vec2) -> Option<C
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn collide_two_rectangles(
|
||||
// (x, y, size x, size y)
|
||||
a: (f32, f32, f32, f32),
|
||||
b: (f32, f32, f32, f32),
|
||||
) -> Option<Collision> {
|
||||
collide(
|
||||
Vec3::new(a.0, a.1, 0.),
|
||||
Vec2::new(a.2, a.3),
|
||||
Vec3::new(b.0, b.1, 0.),
|
||||
Vec2::new(b.2, b.3),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inside_collision() {
|
||||
// Identical
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(1., 1., 1., 1.),
|
||||
(1., 1., 1., 1.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Inside));
|
||||
// B inside A
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(2., 2., 2., 2.),
|
||||
(2., 2., 1., 1.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Inside));
|
||||
// A inside B
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(2., 2., 1., 1.),
|
||||
(2., 2., 2., 2.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Inside));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collision_based_on_b() {
|
||||
// Right of B
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(3., 2., 2., 2.),
|
||||
(2., 2., 2., 2.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Right));
|
||||
// Left of B
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(1., 2., 2., 2.),
|
||||
(2., 2., 2., 2.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Left));
|
||||
// Top of B
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(2., 3., 2., 2.),
|
||||
(2., 2., 2., 2.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Top));
|
||||
// Bottom of B
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(2., 1., 2., 2.),
|
||||
(2., 2., 2., 2.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Bottom));
|
||||
}
|
||||
|
||||
// In case the X-collision depth is equal to the Y-collision depth, always
|
||||
// prefer X-collision, meaning, `Left` or `Right` over `Top` and `Bottom`.
|
||||
#[test]
|
||||
fn prefer_x_collision() {
|
||||
// Bottom-left collision
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(1., 1., 2., 2.),
|
||||
(2., 2., 2., 2.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Left));
|
||||
// Top-left collision
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(1., 3., 2., 2.),
|
||||
(2., 2., 2., 2.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Left));
|
||||
// Bottom-right collision
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(3., 1., 2., 2.),
|
||||
(2., 2., 2., 2.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Right));
|
||||
// Top-right collision
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(3., 3., 2., 2.),
|
||||
(2., 2., 2., 2.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Right));
|
||||
}
|
||||
|
||||
// If the collision intersection area stretches more along the Y-axis then
|
||||
// return `Top` or `Bottom`. Otherwise, `Left` or `Right`.
|
||||
#[test]
|
||||
fn collision_depth_wins() {
|
||||
// Top-right collision
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(3., 3., 2., 2.),
|
||||
(2.5, 2.,2., 2.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Top));
|
||||
// Top-right collision
|
||||
#[rustfmt::skip]
|
||||
let res = collide_two_rectangles(
|
||||
(3., 3., 2., 2.),
|
||||
(2., 2.5, 2., 2.),
|
||||
);
|
||||
assert_eq!(res, Some(Collision::Right));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user