feat: Unchecked and untested plugin base

This commit is contained in:
Alexey 2026-02-13 01:49:45 +03:00
commit 53778f0b33
5 changed files with 145 additions and 62 deletions

128
src/lib.rs Normal file
View file

@ -0,0 +1,128 @@
use bevy::math::bounding::IntersectsVolume;
#[warn(missing_docs)]
use bevy::{math::bounding::{Aabb2d, BoundingCircle}, prelude::*};
// TODO: add more precise crate documentation
// TODO: implement event emitting on collision
/// Simple 2D collision system, inspired by Godot Engine.
#[derive(Component)]
/// Enum of available shapes
pub enum CollisionShape {
/// Rectangle shape
Aabb {
/// The shape itself, tied to **global** position
shape: Aabb2d,
/// Start offset, used to calculate global position.
offset: Vec2,
/// Size of the shape, used to calculate global position.
size: Vec2,
},
/// Circle shape
Circle {
/// The shape itself, tied to **global** position
shape: BoundingCircle,
/// Start offset, used to calculate global position
offset: Vec2,
},
}
impl CollisionShape {
/// Returns true if shapes intersect
pub fn intersects(&self, other: &CollisionShape) -> bool {
match self {
Self::Aabb { shape: aabb, .. } => {
match other {
CollisionShape::Aabb { shape: other_aabb, .. } => aabb.intersects(other_aabb),
CollisionShape::Circle { shape: circle, .. } => aabb.intersects(circle),
}
},
Self::Circle{ shape: circle, .. } => {
match other {
CollisionShape::Aabb { shape: aabb, .. } => circle.intersects(aabb),
CollisionShape::Circle { shape: other_circle, .. } => circle.intersects(other_circle),
}
},
}
}
}
#[derive(Component)]
#[require(Transform)]
/// Component, handling multiple collision shapes for single entity
pub struct Collider {
/// Shapes of the collider
pub shapes: Vec<CollisionShape>,
/// Bitmask of collider groups.
/// Setting this to 0 makes collider invisible for others
pub mask: usize,
/// Bitmask of groups which will be checked for collision.
/// Setting this to 0 makes collider ignorant to others
pub check_mask: usize,
}
impl Collider {
#[inline]
/// Returns true if any of self shapes intersect other's shapes, ignoring groups
pub fn intersects_unchecked(&self, other: &Collider) -> bool {
for shape in self.shapes.iter() {
for other_shape in other.shapes.iter() {
if shape.intersects(other_shape) {
return true;
}
}
}
false
}
/// Returns true if any of self shapes intersect other's shapes, respecting groups.
/// Wrapper around [intersects_unchecked](Self::intersects_unchecked)
pub fn intersects(&self, other: &Collider) -> bool {
if self.check_mask & other.mask == 0 {
return false;
}
self.intersects_unchecked(other)
}
/// Update shapes global position
pub fn update_shapes_position(&mut self, global_position: &Vec2) {
for shape in self.shapes.iter_mut() {
match shape {
CollisionShape::Aabb { shape, offset, size } => {
shape.min.x = global_position.x + offset.x;
shape.min.y = global_position.y + offset.y;
shape.max.x = shape.min.x + size.x;
shape.max.y = shape.min.y + size.y;
},
CollisionShape::Circle { shape, offset } => {
shape.center.x = global_position.x + offset.x;
shape.center.y = global_position.y + offset.y;
},
}
}
}
}
#[derive(Component)]
pub struct UpdateShapes;
/// Update collider shapes to match new global transform
pub fn update_collider_shapes(
mut commands: Commands,
colliders: Query<(&mut Collider, &GlobalTransform, Entity), With<UpdateShapes>>,
) {
for (mut collider, transform, entity) in colliders {
collider.update_shapes_position(&transform.translation().xy());
commands.entity(entity).remove::<UpdateShapes>();
}
}
/// Plugin that adds collision systems
pub struct CollisionPlugin;
impl Plugin for CollisionPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, update_collider_shapes);
}
}