2024-08-23 11:22:09 +00:00
|
|
|
use anyhow::Result;
|
|
|
|
use engine::prelude::*;
|
|
|
|
|
|
|
|
use cgmath::Vector2;
|
|
|
|
|
|
|
|
pub enum CollisionEventType {
|
|
|
|
Block,
|
|
|
|
Ignore,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type HitCallback =
|
2025-02-27 13:14:29 +00:00
|
|
|
dyn FnMut(&mut World, Entity, Entity) -> Result<CollisionEventType> + Send + Sync;
|
2024-08-23 11:22:09 +00:00
|
|
|
|
|
|
|
// HitBox is basically a cylinder
|
|
|
|
pub struct HitBox {
|
|
|
|
pub radius: f32,
|
|
|
|
height: f32,
|
|
|
|
|
|
|
|
hit_event: Option<Box<HitCallback>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HitBox {
|
|
|
|
pub fn from_bb(bounding_box: &BoundingBox) -> Self {
|
|
|
|
Self {
|
|
|
|
radius: Self::calculate_radius(bounding_box),
|
|
|
|
height: bounding_box.max[2] - bounding_box.min[2],
|
|
|
|
|
|
|
|
hit_event: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new(radius: f32, height: f32) -> Self {
|
|
|
|
Self {
|
|
|
|
radius,
|
|
|
|
height,
|
|
|
|
|
|
|
|
hit_event: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn forced(bounding_box: &BoundingBox, radius: f32, height: f32) -> Self {
|
|
|
|
Self {
|
|
|
|
radius: if radius == 0.0 {
|
|
|
|
Self::calculate_radius(bounding_box)
|
|
|
|
} else {
|
|
|
|
radius
|
|
|
|
},
|
|
|
|
height: if height == 0.0 {
|
|
|
|
bounding_box.max[2] - bounding_box.min[2]
|
|
|
|
} else {
|
|
|
|
height
|
|
|
|
},
|
|
|
|
|
|
|
|
hit_event: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn calculate_radius(bounding_box: &BoundingBox) -> f32 {
|
|
|
|
// seek the furthest point
|
|
|
|
let mut radius = 0.0;
|
|
|
|
|
|
|
|
let mut tmp = bounding_box.min[0].abs();
|
|
|
|
|
|
|
|
if tmp > radius {
|
|
|
|
radius = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = bounding_box.min[1].abs();
|
|
|
|
|
|
|
|
if tmp > radius {
|
|
|
|
radius = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = bounding_box.max[0].abs();
|
|
|
|
|
|
|
|
if tmp > radius {
|
|
|
|
radius = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = bounding_box.max[0].abs();
|
|
|
|
|
|
|
|
if tmp > radius {
|
|
|
|
radius = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
radius
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_event<F>(&mut self, f: F)
|
|
|
|
where
|
2025-02-27 13:14:29 +00:00
|
|
|
F: FnMut(&mut World, Entity, Entity) -> Result<CollisionEventType> + Send + Sync + 'static,
|
2024-08-23 11:22:09 +00:00
|
|
|
{
|
|
|
|
self.hit_event = Some(Box::new(f));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_radius(&mut self, radius: f32) {
|
|
|
|
self.radius = radius;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_height(&mut self, height: f32) {
|
|
|
|
self.height = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn height(&self) -> f32 {
|
|
|
|
self.height
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn radius(&self) -> f32 {
|
|
|
|
self.radius
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn check_collision(
|
|
|
|
&self,
|
|
|
|
my_pos: Vector2<f32>,
|
|
|
|
other_radius: f32,
|
|
|
|
other_pos: Vector2<f32>,
|
|
|
|
) -> bool {
|
|
|
|
let d = other_pos - my_pos;
|
|
|
|
let radii = other_radius + self.radius;
|
|
|
|
|
|
|
|
((d.x * d.x) + (d.y * d.y)) <= (radii * radii)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn collision(
|
|
|
|
&mut self,
|
2025-02-27 13:14:29 +00:00
|
|
|
world: &mut World,
|
2024-08-23 11:22:09 +00:00
|
|
|
me: Entity,
|
|
|
|
collider: Entity,
|
|
|
|
) -> Result<CollisionEventType> {
|
|
|
|
match &mut self.hit_event {
|
2025-02-27 13:14:29 +00:00
|
|
|
Some(hit_event) => hit_event(world, me, collider),
|
2024-08-23 11:22:09 +00:00
|
|
|
None => Ok(CollisionEventType::Ignore),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EntityComponent for HitBox {
|
|
|
|
fn name(&self) -> &str {
|
|
|
|
Self::debug_name()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ComponentDebug for HitBox {
|
|
|
|
fn debug_name() -> &'static str {
|
|
|
|
"HitBox"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Clone for HitBox {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
Self {
|
|
|
|
radius: self.radius.clone(),
|
|
|
|
height: self.height.clone(),
|
|
|
|
hit_event: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|