1#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
23pub struct CollisionFilter {
24 pub layer: u32,
26 pub mask: u32,
28 pub group: u32,
30}
31
32impl CollisionFilter {
33 pub const DEFAULT: Self = Self {
35 layer: 1,
36 mask: u32::MAX,
37 group: 0,
38 };
39
40 pub const NONE: Self = Self {
42 layer: 0,
43 mask: 0,
44 group: 0,
45 };
46
47 pub const ALL: Self = Self {
49 layer: u32::MAX,
50 mask: u32::MAX,
51 group: 0,
52 };
53
54 #[inline]
56 #[must_use]
57 pub const fn new(layer: u32, mask: u32) -> Self {
58 Self {
59 layer,
60 mask,
61 group: 0,
62 }
63 }
64
65 #[inline]
67 #[must_use]
68 pub const fn with_group(mut self, group: u32) -> Self {
69 self.group = group;
70 self
71 }
72
73 #[inline]
75 #[must_use]
76 pub const fn can_collide(a: &Self, b: &Self) -> bool {
77 if a.group != 0 && a.group == b.group {
79 return false;
80 }
81 (a.layer & b.mask) != 0 && (b.layer & a.mask) != 0
83 }
84}
85
86impl Default for CollisionFilter {
87 #[inline]
88 fn default() -> Self {
89 Self::DEFAULT
90 }
91}
92
93pub mod layers {
107 pub const DEFAULT: u32 = 1 << 0;
109 pub const STATIC: u32 = 1 << 1;
111 pub const KINEMATIC: u32 = 1 << 2;
113 pub const PLAYER: u32 = 1 << 3;
115 pub const ENEMY: u32 = 1 << 4;
117 pub const PROJECTILE: u32 = 1 << 5;
119 pub const TRIGGER: u32 = 1 << 6;
121 pub const DEBRIS: u32 = 1 << 7;
123 pub const SENSOR: u32 = 1 << 8;
125 pub const VEHICLE: u32 = 1 << 9;
127 pub const ALL: u32 = u32::MAX;
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_default_filter() {
137 let a = CollisionFilter::DEFAULT;
138 let b = CollisionFilter::DEFAULT;
139 assert!(CollisionFilter::can_collide(&a, &b));
140 }
141
142 #[test]
143 fn test_none_filter() {
144 let a = CollisionFilter::NONE;
145 let b = CollisionFilter::DEFAULT;
146 assert!(!CollisionFilter::can_collide(&a, &b));
147 }
148
149 #[test]
150 fn test_layer_mask() {
151 let player = CollisionFilter::new(
152 layers::PLAYER,
153 layers::ENEMY | layers::PROJECTILE | layers::STATIC,
154 );
155 let enemy = CollisionFilter::new(
156 layers::ENEMY,
157 layers::PLAYER | layers::PROJECTILE | layers::STATIC,
158 );
159 let wall = CollisionFilter::new(layers::STATIC, layers::ALL);
160
161 assert!(CollisionFilter::can_collide(&player, &enemy));
162 assert!(CollisionFilter::can_collide(&player, &wall));
163 assert!(CollisionFilter::can_collide(&enemy, &wall));
164 }
165
166 #[test]
167 fn test_one_way_mask() {
168 let a = CollisionFilter::new(1 << 0, 1 << 1);
170 let b = CollisionFilter::new(1 << 1, 0); assert!(!CollisionFilter::can_collide(&a, &b));
172 }
173
174 #[test]
175 fn test_collision_group() {
176 let a = CollisionFilter::new(layers::ALL, layers::ALL).with_group(1);
177 let b = CollisionFilter::new(layers::ALL, layers::ALL).with_group(1);
178 let c = CollisionFilter::new(layers::ALL, layers::ALL).with_group(2);
179
180 assert!(!CollisionFilter::can_collide(&a, &b));
182 assert!(CollisionFilter::can_collide(&a, &c));
184 }
185
186 #[test]
187 fn test_group_zero_always_checks_mask() {
188 let a = CollisionFilter::new(layers::ALL, layers::ALL).with_group(0);
189 let b = CollisionFilter::new(layers::ALL, layers::ALL).with_group(0);
190 assert!(CollisionFilter::can_collide(&a, &b));
191 }
192}