different approach: register collision handlers

This commit is contained in:
Thilo Behnke
2022-04-25 22:06:53 +02:00
parent 9cfd250b81
commit c82c649dc2
6 changed files with 110 additions and 68 deletions

View File

@@ -1,4 +1,6 @@
pub mod collision {
use std::alloc::handle_alloc_error;
use std::collections::HashMap;
use std::fmt::Debug;
use crate::game_object::game_object::GameObject;
use crate::geom::geom::Vector;
@@ -68,25 +70,46 @@ pub mod collision {
#[derive(Debug, Eq, PartialEq)]
pub struct Collision(pub u16, pub u16);
pub struct CollisionHandler {}
pub struct CollisionHandler {
handlers: HashMap<(String, String), fn(&mut Box<dyn GameObject>, &mut Box<dyn GameObject>)>
}
impl CollisionHandler {
pub fn new() -> CollisionHandler {
CollisionHandler {}
}
pub fn handle(&self, obj_a: &mut Box<dyn GameObject>, obj_b: &Box<dyn GameObject>) {
if !obj_a.is_static() {
obj_a.vel_mut().reflect(&obj_b.orientation());
if *obj_b.vel() != Vector::zero() {
let mut adjusted = obj_b.vel().clone();
adjusted.normalize();
obj_a.vel_mut().add(&adjusted);
}
CollisionHandler {
handlers: HashMap::new()
}
let mut b_to_a = obj_a.pos().clone();
b_to_a.sub(&obj_b.pos());
b_to_a.normalize();
obj_a.pos_mut().add(&b_to_a);
}
pub fn register(&mut self, mapping: (String, String), callback: fn(&mut Box<dyn GameObject>, &mut Box<dyn GameObject>)) {
self.handlers.insert(mapping, callback);
}
pub fn handle(&self, obj_a: &mut Box<dyn GameObject>, obj_b: &mut Box<dyn GameObject>) {
let key = (obj_a.obj_type().to_string(), obj_b.obj_type().to_string());
if !self.handlers.contains_key(&key) {
return;
}
let handler = self.handlers[&key];
handler(obj_a, obj_b);
}
// pub fn new() -> CollisionHandler {
// CollisionHandler {}
// }
// pub fn handle(&self, obj_a: &mut Box<dyn GameObject>, obj_b: &Box<dyn GameObject>) {
// if !obj_a.is_static() {
// obj_a.vel_mut().reflect(&obj_b.orientation());
// if *obj_b.vel() != Vector::zero() {
// let mut adjusted = obj_b.vel().clone();
// adjusted.normalize();
// obj_a.vel_mut().add(&adjusted);
// }
// }
// let mut b_to_a = obj_a.pos().clone();
// b_to_a.sub(&obj_b.pos());
// b_to_a.normalize();
// obj_a.pos_mut().add(&b_to_a);
// }
}
}

View File

@@ -103,52 +103,52 @@ impl Field {
ball.obj.update_pos()
}
let mut objs: Vec<&Box<dyn GameObject>> = vec![];
objs.extend(
self.players
.clone()
.into_iter()
.map(|p| &p.obj)
.collect::<Vec<&Box<dyn GameObject>>>(),
);
objs.extend(
self.balls
.clone()
.into_iter()
.map(|b| &b.obj)
.collect::<Vec<&Box<dyn GameObject>>>(),
);
objs.extend(
self.bounds.objs
.clone()
.into_iter()
.collect::<Vec<&Box<dyn GameObject>>>()
);
let collision_detector = CollisionDetector::new();
let collision_handler = CollisionHandler::new();
self.collisions = collision_detector.detect_collisions(&objs);
for ball in self.balls.iter_mut() {
let collisions = self.collisions.get_collisions_by_id(ball.obj.id());
if collisions.is_empty() {
continue;
}
let other = match collisions[0] {
Collision(obj_a_id, obj_b_id) if *obj_a_id == ball.obj.id() => {
objs.iter().find(|o| o.id() == *obj_b_id).unwrap()
}
collision => objs.iter().find(|o| o.id() == collision.0).unwrap(),
};
self.logger.log("### BEFORE COLLISION ###");
self.logger.log(&*format!("{:?}", ball.obj));
self.logger.log(&*format!("{:?}", other));
collision_handler.handle(&mut ball.obj, other);
self.logger.log("### AFTER COLLISION ###");
self.logger.log(&*format!("{:?}", ball.obj));
self.logger.log(&*format!("{:?}", other));
self.logger.log("### DONE ###");
}
// let mut objs: Vec<&Box<dyn GameObject>> = vec![];
// objs.extend(
// self.players
// .clone()
// .into_iter()
// .map(|p| &p.obj)
// .collect::<Vec<&Box<dyn GameObject>>>(),
// );
// objs.extend(
// self.balls
// .clone()
// .into_iter()
// .map(|b| &b.obj)
// .collect::<Vec<&Box<dyn GameObject>>>(),
// );
// objs.extend(
// self.bounds.objs
// .clone()
// .into_iter()
// .collect::<Vec<&Box<dyn GameObject>>>()
// );
// let collision_detector = CollisionDetector::new();
// let collision_handler = CollisionHandler::new();
// self.collisions = collision_detector.detect_collisions(&objs);
//
// for ball in self.balls.iter_mut() {
// let collisions = self.collisions.get_collisions_by_id(ball.obj.id());
// if collisions.is_empty() {
// continue;
// }
// let other = match collisions[0] {
// Collision(obj_a_id, obj_b_id) if *obj_a_id == ball.obj.id() => {
// objs.iter().find(|o| o.id() == *obj_b_id).unwrap()
// }
// collision => objs.iter().find(|o| o.id() == collision.0).unwrap(),
// };
//
// self.logger.log("### BEFORE COLLISION ###");
// self.logger.log(&*format!("{:?}", ball.obj));
// self.logger.log(&*format!("{:?}", other));
// collision_handler.handle(&mut ball.obj, other);
// self.logger.log("### AFTER COLLISION ###");
// self.logger.log(&*format!("{:?}", ball.obj));
// self.logger.log(&*format!("{:?}", other));
// self.logger.log("### DONE ###");
// }
}
pub fn players(&self) -> Vec<&Player> {
@@ -171,6 +171,7 @@ impl Player {
Player {
obj: Box::new(DefaultGameObject::new(
id,
"player".to_string(),
Box::new(DefaultGeomComp::new(
Shape::rect(Vector { x: x as f64, y: y as f64 }, Vector::new(0., 1.), (field.width as f64) / 25., (field.height as f64) / 5.)
)),
@@ -194,6 +195,7 @@ impl Ball {
Ball {
obj: Box::new(DefaultGameObject::new(
id,
"ball".to_string(),
Box::new(DefaultGeomComp::new(
Shape::circle(Vector { x: x as f64, y: y as f64 }, Vector::zero(), (field.width as f64) / 80.)
)),
@@ -218,6 +220,7 @@ impl Bounds {
// top
Box::new(DefaultGameObject::new(
90,
"bound".to_string(),
Box::new(DefaultGeomComp::new(
Shape::rect(Vector {x: (width / 2) as f64, y: 0 as f64}, Vector::new(1., 0.), width as f64, 2.)
)),
@@ -226,6 +229,7 @@ impl Bounds {
// bottom
Box::new(DefaultGameObject::new(
91,
"bound".to_string(),
Box::new(DefaultGeomComp::new(
Shape::rect(Vector {x: (width / 2) as f64, y: height as f64}, Vector::new(-1., 0.), width as f64, 2.)
)),
@@ -234,6 +238,7 @@ impl Bounds {
// left
Box::new(DefaultGameObject::new(
92,
"bound".to_string(),
Box::new(DefaultGeomComp::new(
Shape::rect(Vector {x: 0 as f64, y: (height / 2) as f64}, Vector::new(0., 1.), 2., height as f64)
)),
@@ -242,6 +247,7 @@ impl Bounds {
// right
Box::new(DefaultGameObject::new(
93,
"bound".to_string(),
Box::new(DefaultGeomComp::new(
Shape::rect(Vector {x: width as f64, y: (height / 2) as f64}, Vector::new(0., -1.), 2., height as f64)
)),

View File

@@ -4,8 +4,9 @@ pub mod game_object {
use crate::geom::geom::{BoundingBox, Vector};
use crate::geom::shape::{Shape, ShapeType};
pub trait GameObject : Debug + Clone {
pub trait GameObject : Debug {
fn id(&self) -> u16;
fn obj_type(&self) -> &str;
fn shape(&self) -> &ShapeType;
fn pos(&self) -> &Vector;
fn pos_mut(&mut self) -> &mut Vector;
@@ -18,16 +19,17 @@ pub mod game_object {
}
// #[derive(Clone, Debug, PartialEq)]
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct DefaultGameObject {
pub id: u16,
pub obj_type: String,
geom: Box<dyn GeomComp>,
physics: Box<dyn PhysicsComp>
}
impl DefaultGameObject {
pub fn new(id: u16, geom: Box<dyn GeomComp>, physics: Box<dyn PhysicsComp>) -> DefaultGameObject {
DefaultGameObject {id, geom, physics}
pub fn new(id: u16, obj_type: String, geom: Box<dyn GeomComp>, physics: Box<dyn PhysicsComp>) -> DefaultGameObject {
DefaultGameObject {id, obj_type, geom, physics}
}
}
@@ -36,6 +38,10 @@ pub mod game_object {
self.id
}
fn obj_type(&self) -> &str {
&self.obj_type
}
fn shape(&self) -> &ShapeType {
self.geom.shape()
}

View File

@@ -64,12 +64,12 @@ use pong::geom::shape::Shape;
)]
pub fn should_handle_collision(
#[case] mut obj_a: Box<dyn GameObject>,
#[case] obj_b: Box<dyn GameObject>,
#[case] mut obj_b: Box<dyn GameObject>,
#[case] expected_a: Box<dyn GameObject>,
#[case] expected_b: Box<dyn GameObject>,
) {
let handler = CollisionHandler {};
handler.handle(&mut obj_a, &obj_b);
let handler = CollisionHandler::new();
handler.handle(&mut obj_a, &mut obj_b);
assert_eq!(obj_a.pos(), expected_a.pos());
assert_eq!(obj_a.vel(), expected_a.vel());
assert_eq!(obj_b.pos(), expected_b.pos());
@@ -79,6 +79,7 @@ pub fn should_handle_collision(
fn create_game_obj(id: u16, vel: Vector, orientation: Vector, is_static: bool) -> Box<dyn GameObject> {
Box::new(DefaultGameObject::new(
id,
"obj".to_string(),
Box::new(DefaultGeomComp::new(
Shape::rect(Vector::zero(), orientation, 20., 20.)
)),

View File

@@ -69,6 +69,11 @@ impl GameObject for MockGameObject {
self.id
}
fn obj_type(&self) -> &str {
todo!()
}
fn shape(&self) -> &ShapeType {
todo!()
}

View File

@@ -9,6 +9,7 @@ use pong::geom::shape::Shape;
pub fn should_update_pos(#[case] start_pos: Vector, #[case] vel: Vector, #[case] expected_pos: Vector) {
let mut obj = DefaultGameObject::new(
1,
"obj".to_string(),
Box::new(DefaultGeomComp::new(
Shape::rect(Vector::new(start_pos.x as f64, start_pos.y as f64), Vector::new(1., 0.), 0., 0.)
)),