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};
|
use bevy_math::{Vec2, Vec3};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Collision {
|
pub enum Collision {
|
||||||
Left,
|
Left,
|
||||||
Right,
|
Right,
|
||||||
@ -16,6 +16,11 @@ pub enum Collision {
|
|||||||
/// * `a_pos` and `b_pos` are the center positions of the rectangles, typically obtained by
|
/// * `a_pos` and `b_pos` are the center positions of the rectangles, typically obtained by
|
||||||
/// extracting the `translation` field from a `Transform` component
|
/// extracting the `translation` field from a `Transform` component
|
||||||
/// * `a_size` and `b_size` are the dimensions (width and height) of the rectangles.
|
/// * `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> {
|
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_min = a_pos.truncate() - a_size / 2.0;
|
||||||
let a_max = 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
|
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