use bevy::{
ecs::component::Component,
reflect::{FromReflect, Reflect},
};
#[allow(missing_docs)]
pub trait PhysicsLayer: Sized {
fn to_bits(&self) -> u32;
fn all_bits() -> u32;
}
impl<T: PhysicsLayer> PhysicsLayer for &T {
fn to_bits(&self) -> u32 {
T::to_bits(self)
}
fn all_bits() -> u32 {
T::all_bits()
}
}
#[derive(Debug, Component, Copy, Clone, Eq, PartialEq, Reflect, FromReflect)]
pub struct CollisionLayers {
groups: u32,
masks: u32,
}
impl Default for CollisionLayers {
fn default() -> Self {
Self {
groups: 0xffff_ffff,
masks: 0xffff_ffff,
}
}
}
impl CollisionLayers {
#[must_use]
pub fn new<L: PhysicsLayer>(group: L, mask: L) -> Self {
Self::from_bits(group.to_bits(), mask.to_bits())
}
#[must_use]
pub fn all<L: PhysicsLayer>() -> Self {
Self::from_bits(L::all_bits(), L::all_bits())
}
#[must_use]
pub fn all_groups<L: PhysicsLayer>() -> Self {
Self::from_bits(L::all_bits(), 0)
}
#[must_use]
pub fn all_masks<L: PhysicsLayer>() -> Self {
Self::from_bits(0, L::all_bits())
}
#[must_use]
pub const fn none() -> Self {
Self::from_bits(0, 0)
}
#[must_use]
#[allow(missing_docs)]
pub const fn from_bits(groups: u32, masks: u32) -> Self {
Self { groups, masks }
}
#[must_use]
pub fn interacts_with(self, other: Self) -> bool {
(self.groups & other.masks) != 0 && (other.groups & self.masks) != 0
}
#[must_use]
pub fn contains_group(self, layer: impl PhysicsLayer) -> bool {
(self.groups & layer.to_bits()) != 0
}
#[must_use]
pub fn with_group(mut self, layer: impl PhysicsLayer) -> Self {
self.groups |= layer.to_bits();
self
}
#[must_use]
pub fn with_groups(mut self, layers: impl IntoIterator<Item = impl PhysicsLayer>) -> Self {
for layer in layers.into_iter().map(|l| l.to_bits()) {
self.groups |= layer;
}
self
}
#[must_use]
pub fn without_group(mut self, layer: impl PhysicsLayer) -> Self {
self.groups &= !layer.to_bits();
self
}
#[must_use]
pub fn contains_mask(self, layer: impl PhysicsLayer) -> bool {
(self.masks & layer.to_bits()) != 0
}
#[must_use]
pub fn with_mask(mut self, layer: impl PhysicsLayer) -> Self {
self.masks |= layer.to_bits();
self
}
#[must_use]
pub fn with_masks(mut self, layers: impl IntoIterator<Item = impl PhysicsLayer>) -> Self {
for layer in layers.into_iter().map(|l| l.to_bits()) {
self.masks |= layer;
}
self
}
#[must_use]
pub fn without_mask(mut self, layer: impl PhysicsLayer) -> Self {
self.masks &= !layer.to_bits();
self
}
#[must_use]
#[allow(missing_docs)]
pub fn groups_bits(self) -> u32 {
self.groups
}
#[must_use]
#[allow(missing_docs)]
pub fn masks_bits(self) -> u32 {
self.masks
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
enum TestLayer {
One,
Two,
}
impl PhysicsLayer for TestLayer {
fn to_bits(&self) -> u32 {
match self {
TestLayer::One => 1,
TestLayer::Two => 2,
}
}
fn all_bits() -> u32 {
3
}
}
#[test]
fn all_interacts_with_all() {
assert!(
CollisionLayers::all::<TestLayer>().interacts_with(CollisionLayers::all::<TestLayer>())
);
}
#[rstest]
#[case(CollisionLayers::all::<TestLayer>())]
#[case(CollisionLayers::none())]
#[case(CollisionLayers::all_groups::<TestLayer>())]
#[case(CollisionLayers::all_masks::<TestLayer>())]
fn none_does_not_interact_with_any_anything(#[case] other: CollisionLayers) {
assert!(!CollisionLayers::none().interacts_with(other));
assert!(!other.interacts_with(CollisionLayers::none()));
}
#[test]
fn empty_groups_and_masks_not_interact() {
let c1 = CollisionLayers::all_groups::<TestLayer>();
let c2 = CollisionLayers::all_masks::<TestLayer>();
assert!(!c1.interacts_with(c2));
assert!(!c2.interacts_with(c1));
}
#[test]
fn with_layer_adds_interaction() {
let c1 = CollisionLayers::none()
.with_group(TestLayer::One)
.with_mask(TestLayer::Two);
let c2 = CollisionLayers::none()
.with_group(TestLayer::Two)
.with_mask(TestLayer::One);
assert!(c1.interacts_with(c2));
assert!(c2.interacts_with(c1));
assert!(!c1.interacts_with(c1));
assert!(!c2.interacts_with(c2));
}
#[test]
fn without_layer_removes_interaction() {
let c1 = CollisionLayers::all::<TestLayer>()
.without_group(TestLayer::One)
.without_mask(TestLayer::Two);
let c2 = CollisionLayers::all::<TestLayer>()
.without_group(TestLayer::Two)
.without_mask(TestLayer::One);
println!("{:?}, {:?}", c1, c2);
assert!(c1.interacts_with(c2));
assert!(c2.interacts_with(c1));
assert!(!c1.interacts_with(c1));
assert!(!c2.interacts_with(c2));
}
}