app.add_event::<CollisionEvent>()
.add_systems(Update, debug_log_collisions)
.add_systems(FixedUpdate, resolve_collisions.after(movement::Movement));
}
}
#[derive(Debug, Event)]
pub struct CollisionEvent {
pub object_a: Entity,
pub object_b: Entity,
}
#[derive(Component, Default)]
pub struct Collidable {
pub offset: Vec2,
pub extents: Vec2,
/// Can this object be moved by collision? true if not
pub is_static: bool,
}
fn debug_log_collisions(mut reader: EventReader<CollisionEvent>) {
for event in reader.read() {
info!(?event, "Collision Event!");
}
}
fn resolve_collisions(
mut query: Query<(Entity, &GlobalTransform, &mut Transform, &mut Collidable)>,
mut writer: EventWriter<CollisionEvent>,
) {
let mut iter = query.iter_combinations_mut();
while let Some(
[(entity, gtransform, mut transform, collidable), (oentity, ogtransform, mut otransform, ocollidable)],
) = iter.fetch_next()
{
let aabb = Aabb2d::new(
gtransform.translation().truncate() + collidable.offset,
collidable.extents / 2.,
);
let oaabb = Aabb2d::new(
ogtransform.translation().truncate() + ocollidable.offset,
ocollidable.extents / 2.,
);
if let Some(mtv) = sat_collision_aabb(aabb, oaabb) {
let (magnitude, axis) = (mtv.length(), mtv.normalize());
let aabb_min_proj = aabb.min.project_onto_normalized(axis);
let oaabb_min_proj = oaabb.min.project_onto_normalized(axis);
let mtv = (axis * magnitude * (oaabb_min_proj - aabb_min_proj).signum()).extend(0.0);
match (collidable.is_static, ocollidable.is_static) {
// If both are static, continue w/o emitting an event
(true, true) => continue,
(true, false) => {
// Move collidable entity
otransform.translation += mtv;
}
(false, true) => {
// Ditto but ocollidable
transform.translation += mtv;
}
(false, false) => {
// Split the difference
// TODO Implement mass, so that we can scale this
transform.translation -= mtv / 2.;
otransform.translation += mtv / 2.;
}
}
writer.send(CollisionEvent {
object_a: entity,
object_b: oentity,
});
}
}
}
// Projection is (min, max)
fn aabb_project(shape: Aabb2d, axis: Vec2) -> Option<(f32, f32)> {
let norm = axis.try_normalize()?;
let points = [
shape.min,
Vec2::new(shape.max.x, shape.min.y),
Vec2::new(shape.min.x, shape.max.y),
shape.max,
];
let mut min = norm.dot(points[0]);
let mut max = min;
for point in &points[1..] {
let v = norm.dot(*point);
min = min.min(v);
max = max.max(v);