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

14
Cargo.lock generated
View file

@ -532,6 +532,13 @@ dependencies = [
"wgpu-types", "wgpu-types",
] ]
[[package]]
name = "bevy_collision_plugin"
version = "0.1.0"
dependencies = [
"bevy",
]
[[package]] [[package]]
name = "bevy_color" name = "bevy_color"
version = "0.18.0" version = "0.18.0"
@ -1377,13 +1384,6 @@ dependencies = [
"pin-project", "pin-project",
] ]
[[package]]
name = "bevy_template"
version = "0.1.0"
dependencies = [
"bevy",
]
[[package]] [[package]]
name = "bevy_text" name = "bevy_text"
version = "0.18.0" version = "0.18.0"

View file

@ -1,7 +1,7 @@
cargo-features = ["codegen-backend"] cargo-features = ["codegen-backend"]
[package] [package]
name = "bevy_template" name = "bevy_collision_plugin"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"

9
examples/example.rs Normal file
View file

@ -0,0 +1,9 @@
use bevy::prelude::*;
use bevy_collision_plugin::CollisionPlugin;
fn main() {
App::new()
.add_plugins((DefaultPlugins, CollisionPlugin))
.run();
}

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);
}
}

View file

@ -1,54 +0,0 @@
use bevy::prelude::*;
#[derive(Component)]
struct Person;
#[derive(Component)]
struct Name(String);
#[derive(Resource)]
struct GreetTimer(Timer);
#[derive(Resource)]
struct UpdateTimer(Timer);
pub struct HelloPlugin;
impl Plugin for HelloPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(GreetTimer(Timer::from_seconds(2.0, TimerMode::Repeating)));
app.insert_resource(UpdateTimer(Timer::from_seconds(10.0, TimerMode::Once)));
app.add_systems(Startup, add_people);
app.add_systems(Update, (update_people, greet_people).chain());
}
}
fn add_people(mut commands: Commands) {
commands.spawn((Person, Name("Alkesey Mirnekov".to_string())));
commands.spawn((Person, Name("Alkesey Mirnekov 2".to_string())));
commands.spawn((Person, Name("Alkesey Mirnekov 3".to_string())));
}
fn greet_people(time: Res<Time>, mut timer: ResMut<GreetTimer>, query: Query<&Name, With<Person>>) {
if timer.0.tick(time.delta()).just_finished() {
for name in &query {
println!("hello {}!", name.0);
}
}
}
fn update_people(time: Res<Time>, mut timer: ResMut<UpdateTimer>, mut query: Query<&mut Name, With<Person>>) {
if timer.0.tick(time.delta()).just_finished() {
for mut name in &mut query {
name.0 = format!("{} II", name.0);
}
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(HelloPlugin)
.run();
}