mirror of
https://github.com/thilo-behnke/wasm-pong.git
synced 2026-02-14 14:39:51 +00:00
bugfix/avoid-player-crushing-ball-outside-of-field
This commit is contained in:
@@ -242,6 +242,10 @@ pub mod handler {
|
||||
handlers: CollisionHandlerRegistry,
|
||||
}
|
||||
|
||||
pub struct FieldStats {
|
||||
pub dimensions: (f64, f64)
|
||||
}
|
||||
|
||||
impl CollisionHandler {
|
||||
pub fn new(logger_factory: &Box<dyn LoggerFactory>) -> CollisionHandler {
|
||||
let logger = logger_factory.get("collision_handler");
|
||||
@@ -254,13 +258,14 @@ pub mod handler {
|
||||
pub fn register(
|
||||
&mut self,
|
||||
mapping: (String, String),
|
||||
callback: fn(&Rc<RefCell<Box<dyn GameObject>>>, &Rc<RefCell<Box<dyn GameObject>>>),
|
||||
callback: fn(&FieldStats, &Rc<RefCell<Box<dyn GameObject>>>, &Rc<RefCell<Box<dyn GameObject>>>),
|
||||
) {
|
||||
self.handlers.add(mapping, callback)
|
||||
}
|
||||
|
||||
pub fn handle(
|
||||
&self,
|
||||
stats: &FieldStats,
|
||||
obj_a: &Rc<RefCell<Box<dyn GameObject>>>,
|
||||
obj_b: &Rc<RefCell<Box<dyn GameObject>>>,
|
||||
) -> bool {
|
||||
@@ -268,7 +273,7 @@ pub mod handler {
|
||||
RefCell::borrow(&obj_a).obj_type().to_string(),
|
||||
RefCell::borrow(&obj_b).obj_type().to_string(),
|
||||
);
|
||||
let handler_res = self.handlers.call(&key, (&obj_a, &obj_b));
|
||||
let handler_res = self.handlers.call(&key, (&stats, &obj_a, &obj_b));
|
||||
if !handler_res {
|
||||
self.logger
|
||||
.log(&*format!("Found no matching collision handler: {:?}", key));
|
||||
@@ -299,12 +304,12 @@ pub mod handler {
|
||||
pub struct CollisionHandlerRegistry {
|
||||
handlers: HashMap<
|
||||
(String, String),
|
||||
fn(&Rc<RefCell<Box<dyn GameObject>>>, &Rc<RefCell<Box<dyn GameObject>>>),
|
||||
fn(&FieldStats, &Rc<RefCell<Box<dyn GameObject>>>, &Rc<RefCell<Box<dyn GameObject>>>),
|
||||
>,
|
||||
}
|
||||
|
||||
type CollisionCallback =
|
||||
fn(&Rc<RefCell<Box<dyn GameObject>>>, &Rc<RefCell<Box<dyn GameObject>>>);
|
||||
fn(&FieldStats, &Rc<RefCell<Box<dyn GameObject>>>, &Rc<RefCell<Box<dyn GameObject>>>);
|
||||
|
||||
impl CollisionHandlerRegistry {
|
||||
pub fn new() -> CollisionHandlerRegistry {
|
||||
@@ -327,18 +332,19 @@ pub mod handler {
|
||||
&self,
|
||||
mapping: &(String, String),
|
||||
values: (
|
||||
&FieldStats,
|
||||
&Rc<RefCell<Box<dyn GameObject>>>,
|
||||
&Rc<RefCell<Box<dyn GameObject>>>,
|
||||
),
|
||||
) -> bool {
|
||||
let regular = self.handlers.get(&mapping);
|
||||
if let Some(callback) = regular {
|
||||
callback(values.0, values.1);
|
||||
callback(values.0, values.1, values.2);
|
||||
return true;
|
||||
}
|
||||
let inverse = self.handlers.get(&(mapping.clone().1, mapping.clone().0));
|
||||
if let Some(callback) = inverse {
|
||||
callback(values.1, values.0);
|
||||
callback(values.0, values.2, values.1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -350,7 +356,7 @@ pub mod handler {
|
||||
use rstest::rstest;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use crate::collision::handler::CollisionHandler;
|
||||
use crate::collision::handler::{CollisionHandler, FieldStats};
|
||||
use crate::game_object::components::{DefaultGeomComp, DefaultPhysicsComp};
|
||||
use crate::game_object::game_object::{DefaultGameObject, GameObject};
|
||||
use crate::geom::shape::Shape;
|
||||
@@ -392,14 +398,17 @@ pub mod handler {
|
||||
) {
|
||||
let logger = DefaultLoggerFactory::noop();
|
||||
let mut handler = CollisionHandler::new(&logger);
|
||||
handler.register((String::from("obj"), String::from("obj")), |_a, _b| {
|
||||
let field_stats = FieldStats {
|
||||
dimensions: (1000., 1000.)
|
||||
};
|
||||
handler.register((String::from("obj"), String::from("obj")), |_stats, _a, _b| {
|
||||
let mut a_mut = RefCell::borrow_mut(_a);
|
||||
let mut vel_inverted = a_mut.vel().clone();
|
||||
vel_inverted.invert();
|
||||
*a_mut.vel_mut() = vel_inverted;
|
||||
});
|
||||
let expected_vel_a = Vector::inverted(RefCell::borrow(&obj_a).vel());
|
||||
let res = handler.handle(&obj_a, &obj_b);
|
||||
let res = handler.handle(&field_stats, &obj_a, &obj_b);
|
||||
assert_eq!(true, res);
|
||||
assert_eq!(RefCell::borrow(&obj_a).pos(), RefCell::borrow(&obj_a).pos());
|
||||
assert_eq!(RefCell::borrow(&obj_a).vel(), &expected_vel_a);
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::collision::collision::{
|
||||
CollisionRegistry, Collisions,
|
||||
};
|
||||
use crate::collision::detection::{CollisionDetector, CollisionGroup};
|
||||
use crate::collision::handler::{CollisionHandler};
|
||||
use crate::collision::handler::{CollisionHandler, FieldStats};
|
||||
use crate::game_object::components::{DefaultGeomComp, DefaultPhysicsComp};
|
||||
use crate::game_object::game_object::{DefaultGameObject, GameObject};
|
||||
use crate::geom::shape::Shape;
|
||||
@@ -193,7 +193,8 @@ impl Field {
|
||||
.find(|o| RefCell::borrow(o).id() == collision.1)
|
||||
.unwrap()
|
||||
.clone();
|
||||
collision_handler.handle(&obj_a, &obj_b);
|
||||
let field_stats = FieldStats {dimensions: (self.width as f64, self.height as f64)};
|
||||
collision_handler.handle(&field_stats, &obj_a, &obj_b);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -471,6 +471,22 @@ pub mod shape {
|
||||
Circle(Shape, f64),
|
||||
}
|
||||
|
||||
impl ShapeType {
|
||||
pub fn dimensions(&self) -> Vector {
|
||||
return Vector::new(self.width().clone(), self.height().clone());
|
||||
}
|
||||
pub fn width(&self) -> &f64 {
|
||||
match self {
|
||||
ShapeType::Rect(_, width, _) | ShapeType::Circle(_, width) => width,
|
||||
}
|
||||
}
|
||||
pub fn height(&self) -> &f64 {
|
||||
match self {
|
||||
ShapeType::Rect(_, _, height) | ShapeType::Circle(_, height) => height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Shape {
|
||||
center: Vector,
|
||||
|
||||
@@ -6,14 +6,29 @@ pub mod pong_collisions {
|
||||
use std::cmp::min;
|
||||
use std::f64::consts::{FRAC_PI_2, FRAC_PI_4, PI};
|
||||
use std::rc::Rc;
|
||||
use crate::collision::handler::FieldStats;
|
||||
use crate::utils::number_utils::is_in_range;
|
||||
|
||||
pub fn handle_player_ball_collision(
|
||||
stats: &FieldStats,
|
||||
ball: &Rc<RefCell<Box<dyn GameObject>>>,
|
||||
player: &Rc<RefCell<Box<dyn GameObject>>>,
|
||||
) {
|
||||
let mut ball = RefCell::borrow_mut(&ball);
|
||||
let player = player.borrow();
|
||||
|
||||
// player is crushing the ball out of bounds
|
||||
let ball_pos = ball.pos().clone();
|
||||
let ball_height = {
|
||||
let ball_dimensions = ball.shape().dimensions();
|
||||
ball_dimensions.y
|
||||
};
|
||||
if is_in_range(ball_pos.y, stats.dimensions.1 - ball_height / 2., stats.dimensions.1 + ball_height / 2.) || is_in_range(ball_pos.y, 0. - ball_height / 2., 0. + ball_height / 2.) {
|
||||
let mut player = player.borrow_mut();
|
||||
*player.vel_mut() = Vector::zero();
|
||||
return;
|
||||
}
|
||||
|
||||
let player = player.borrow();
|
||||
// reflect
|
||||
let ball_vel = ball.vel_mut();
|
||||
let mut ball_vel_total = ball_vel.len();
|
||||
@@ -48,10 +63,13 @@ pub mod pong_collisions {
|
||||
}
|
||||
|
||||
pub fn handle_ball_bounds_collision(
|
||||
_stats: &FieldStats,
|
||||
ball: &Rc<RefCell<Box<dyn GameObject>>>,
|
||||
bound: &Rc<RefCell<Box<dyn GameObject>>>,
|
||||
) {
|
||||
let mut ball = RefCell::borrow_mut(&ball);
|
||||
let mut ball_dimensions = ball.shape().dimensions();
|
||||
ball_dimensions.scalar_multiplication(0.5);
|
||||
let bound = RefCell::borrow(&bound);
|
||||
ball.vel_mut().reflect(&bound.orientation());
|
||||
|
||||
@@ -63,15 +81,17 @@ pub mod pong_collisions {
|
||||
bound_pos.multiply(&bound_orientation);
|
||||
|
||||
let mut b_to_a = ball.pos().clone();
|
||||
b_to_a.multiply(&bound_orientation);
|
||||
b_to_a.sub(&bound_pos);
|
||||
b_to_a.normalize();
|
||||
b_to_a.scalar_multiplication(5.);
|
||||
b_to_a.multiply(&ball_dimensions);
|
||||
ball.pos_mut().add(&b_to_a);
|
||||
|
||||
ball.set_dirty(true);
|
||||
}
|
||||
|
||||
pub fn handle_player_bound_collision(
|
||||
_stats: &FieldStats,
|
||||
player: &Rc<RefCell<Box<dyn GameObject>>>,
|
||||
bound: &Rc<RefCell<Box<dyn GameObject>>>,
|
||||
) {
|
||||
@@ -98,6 +118,7 @@ pub mod pong_collisions {
|
||||
use rstest::rstest;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use crate::collision::handler::FieldStats;
|
||||
use crate::game_field::{Bound, Field};
|
||||
use crate::game_object::game_object::{DefaultGameObject, GameObject};
|
||||
use crate::geom::vector::Vector;
|
||||
@@ -144,7 +165,8 @@ pub mod pong_collisions {
|
||||
#[case] player_expected: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
#[case] bounds_expected: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
) {
|
||||
handle_player_bound_collision(&player, &bounds);
|
||||
let stats = FieldStats {dimensions: (1000., 1000.)};
|
||||
handle_player_bound_collision(&stats, &player, &bounds);
|
||||
assert_eq!(player_expected.borrow().pos(), player.borrow().pos());
|
||||
assert_eq!(bounds_expected.borrow().pos(), bounds.borrow().pos());
|
||||
}
|
||||
|
||||
@@ -45,3 +45,9 @@ pub mod utils {
|
||||
fn log(&self, _msg: &str) {}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod number_utils {
|
||||
pub fn is_in_range(n: f64, from: f64, to: f64) -> bool {
|
||||
return from <= n && n <= to;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user