From deb0eb9af4710b798046317eeb63d0eb2f06dc53 Mon Sep 17 00:00:00 2001 From: Thilo Behnke Date: Mon, 18 Apr 2022 22:42:49 +0200 Subject: [PATCH] refactoring --- src/game_object.rs | 56 ++++++++++ src/geom.rs | 77 +++++++++++++ src/lib.rs | 270 ++++++++++++--------------------------------- 3 files changed, 206 insertions(+), 197 deletions(-) create mode 100644 src/game_object.rs create mode 100644 src/geom.rs diff --git a/src/game_object.rs b/src/game_object.rs new file mode 100644 index 0000000..ebb4c92 --- /dev/null +++ b/src/game_object.rs @@ -0,0 +1,56 @@ +use crate::{BoundingBox, Vector}; + +#[derive(Clone, Debug, PartialEq)] +pub enum Shape { + Rect = 0, + Circle = 1, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct GameObject { + pub id: u16, + pub x: u16, + pub y: u16, + pub shape: Shape, + pub shape_params: Vec, + pub vel: Vector, + pub is_static: bool +} + +impl GameObject { + pub fn update_pos(&mut self, field_width: u16, field_height: u16) { + let updated_x = self.x.wrapping_add(self.vel.x as u16); + let updated_y = self.y.wrapping_add(self.vel.y as u16); + + let updated_bounding_box = self.bounding_box_from(updated_x, updated_y); + if updated_bounding_box.points().iter().any(|p| p.x < 0 || p.x > field_width as i16 || p.y < 0 || p.y > field_height as i16) { + return; + } + self.x = updated_x; + self.y = updated_y; + } + + pub fn set_vel_x(&mut self, x: i32) { + self.vel.x = x + } + + pub fn set_vel_y(&mut self, y: i32) { + self.vel.y = y + } + + pub fn bounding_box(&self) -> BoundingBox { + self.bounding_box_from(self.x, self.y) + } + + fn bounding_box_from(&self, x: u16, y: u16) -> BoundingBox { + match self.shape { + Shape::Rect => { + BoundingBox::create(x, y, self.shape_params[0], self.shape_params[1]) + }, + Shape::Circle => { + BoundingBox::create(x, y, self.shape_params[0] * 2, self.shape_params[0] * 2) + } + } + } +} + diff --git a/src/geom.rs b/src/geom.rs new file mode 100644 index 0000000..917c6b6 --- /dev/null +++ b/src/geom.rs @@ -0,0 +1,77 @@ +pub mod geom { + #[derive(Debug, Clone, PartialEq)] + pub struct Vector { + pub x: i32, + pub y: i32 + } + + impl Vector { + pub fn zero() -> Vector { + Vector {x: 0, y: 0} + } + + pub fn unit() -> Vector { + Vector {x: 1, y: 1} + } + + pub fn normalize(&mut self) { + let length = self.len(); + self.x /= length; + self.y /= length; + } + + pub fn invert(&mut self) { + self.x = self.x * -1; + self.y = self.y * -1; + } + + pub fn len(&self) -> i32 { + let distance = self.x.pow(2) + self.y.pow(2); + return (distance as f32).sqrt() as i32; + } + } + + pub struct BoundingBox { + top_left: Point, + top_right: Point, + bottom_left: Point, + bottom_right: Point + } + + impl BoundingBox { + pub fn create(center_x: u16, center_y: u16, width: u16, height: u16) -> BoundingBox { + let top_left = Point {x: center_x as i16 - (width / 2) as i16, y: center_y as i16 + (height / 2) as i16}; + let top_right = Point {x: center_x as i16 + (width / 2) as i16, y: center_y as i16 + (height / 2) as i16}; + let bottom_left = Point {x: center_x as i16 - (width / 2) as i16, y: center_y as i16 - (height / 2) as i16}; + let bottom_right = Point {x: center_x as i16 + (width / 2) as i16, y: center_y as i16 - (height / 2) as i16}; + BoundingBox { + top_left, top_right, bottom_left, bottom_right + } + } + + pub fn points(&self) -> Vec<&Point> { + return vec![ + &self.top_left, &self.top_right, &self.bottom_left, &self.bottom_right + ] + } + + pub fn overlaps(&self, other: &BoundingBox) -> bool { + return other.points().iter().any(|p| self.is_point_within(p)) + } + + pub fn is_point_within(&self, point: &Point) -> bool { + return point.x >= self.top_left.x && point.y <= self.top_left.y && point.y >= self.bottom_left.y + } + } + + pub struct Point { + pub x: i16, + pub y: i16 + } + + impl Point { + pub fn create(x: i16, y: i16) -> Point { + Point { x, y } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index dc797b7..b1b2589 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,14 @@ mod utils; +mod geom; +mod game_object; use std::borrow::BorrowMut; use std::cmp::{max, min}; use serde::{Deserialize, Serialize}; use serde_json::json; use wasm_bindgen::prelude::*; +use crate::game_object::{GameObject, Shape}; +use crate::geom::geom::{BoundingBox, Vector}; extern crate serde_json; extern crate web_sys; @@ -22,142 +26,6 @@ macro_rules! log { #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; -#[wasm_bindgen] -pub struct Field { - pub width: u16, - pub height: u16, - players: Vec, - balls: Vec, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Vector { - pub x: i32, - pub y: i32 -} - -impl Vector { - pub fn zero() -> Vector { - Vector {x: 0, y: 0} - } - - pub fn normalize(&mut self) { - let length = self.len(); - self.x /= length; - self.y /= length; - } - - pub fn invert(&mut self) { - self.x = self.x * -1; - self.y = self.y * -1; - } - - pub fn len(&self) -> i32 { - let distance = self.x.pow(2) + self.y.pow(2); - return (distance as f32).sqrt() as i32; - } -} - -#[wasm_bindgen] -#[repr(u8)] -#[derive(Clone, Debug, PartialEq)] -pub enum Shape { - Rect = 0, - Circle = 1, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct GameObject { - pub id: u16, - pub x: u16, - pub y: u16, - pub shape: Shape, - pub shape_params: Vec, - pub vel: Vector, - pub is_static: bool -} - -impl GameObject { - pub fn update_pos(&mut self, field_width: u16, field_height: u16) { - let updated_x = self.x.wrapping_add(self.vel.x as u16); - let updated_y = self.y.wrapping_add(self.vel.y as u16); - - let updated_bounding_box = self.bounding_box_from(updated_x, updated_y); - if updated_bounding_box.points().iter().any(|p| p.x < 0 || p.x > field_width as i16 || p.y < 0 || p.y > field_height as i16) { - return; - } - self.x = updated_x; - self.y = updated_y; - } - - pub fn set_vel_x(&mut self, x: i32) { - self.vel.x = x - } - - pub fn set_vel_y(&mut self, y: i32) { - self.vel.y = y - } - - pub fn bounding_box(&self) -> BoundingBox { - self.bounding_box_from(self.x, self.y) - } - - fn bounding_box_from(&self, x: u16, y: u16) -> BoundingBox { - match self.shape { - Shape::Rect => { - BoundingBox::create(x, y, self.shape_params[0], self.shape_params[1]) - }, - Shape::Circle => { - BoundingBox::create(x, y, self.shape_params[0] * 2, self.shape_params[0] * 2) - } - } - } -} - -pub struct BoundingBox { - top_left: Point, - top_right: Point, - bottom_left: Point, - bottom_right: Point -} - -impl BoundingBox { - pub fn create(center_x: u16, center_y: u16, width: u16, height: u16) -> BoundingBox { - let top_left = Point {x: center_x as i16 - (width / 2) as i16, y: center_y as i16 + (height / 2) as i16}; - let top_right = Point {x: center_x as i16 + (width / 2) as i16, y: center_y as i16 + (height / 2) as i16}; - let bottom_left = Point {x: center_x as i16 - (width / 2) as i16, y: center_y as i16 - (height / 2) as i16}; - let bottom_right = Point {x: center_x as i16 + (width / 2) as i16, y: center_y as i16 - (height / 2) as i16}; - BoundingBox { - top_left, top_right, bottom_left, bottom_right - } - } - - pub fn points(&self) -> Vec<&Point> { - return vec![ - &self.top_left, &self.top_right, &self.bottom_left, &self.bottom_right - ] - } - - pub fn overlaps(&self, other: &BoundingBox) -> bool { - return other.points().iter().any(|p| self.is_point_within(p)) - } - - pub fn is_point_within(&self, point: &Point) -> bool { - return point.x >= self.top_left.x && point.y <= self.top_left.y && point.y >= self.bottom_left.y - } -} - -pub struct Point { - x: i16, - y: i16 -} - -impl Point { - pub fn create(x: i16, y: i16) -> Point { - Point { x, y } - } -} - #[wasm_bindgen] #[repr(packed)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] @@ -183,48 +51,6 @@ impl GameObjectDTO { } } -#[derive(Clone, Debug, PartialEq)] -pub struct Player { - pub obj: GameObject, -} - -impl Player { - pub fn new(id: u16, x: u16, y: u16, field: &Field) -> Player { - Player { - obj: GameObject { - id, - x, - y, - shape: Shape::Rect, - shape_params: vec![field.width / 25, field.height / 5], - vel: Vector::zero(), - is_static: true - }, - } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub struct Ball { - pub obj: GameObject, -} - -impl Ball { - pub fn new(id: u16, x: u16, y: u16, field: &Field) -> Ball { - Ball { - obj: GameObject { - id, - x, - y, - shape: Shape::Circle, - shape_params: vec![field.width / 80], - vel: Vector::zero(), - is_static: false - }, - } - } -} - #[wasm_bindgen] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum InputType { @@ -239,6 +65,14 @@ pub struct Input { pub obj_id: u16, } +#[wasm_bindgen] +pub struct Field { + pub width: u16, + pub height: u16, + players: Vec, + balls: Vec, +} + #[wasm_bindgen] impl Field { pub fn new() -> Field { @@ -349,34 +183,34 @@ impl Field { let players = &self.players; let balls = &self.balls; // TODO: Find obj by id. - if !collision.obj_a.is_static { - collision.obj_a.vel.invert(); - } - if !collision.obj_b.is_static { - collision.obj_b.vel.invert(); - } + // if !collision.obj_a.is_static { + // collision.obj_a.vel.invert(); + // } + // if !collision.obj_b.is_static { + // collision.obj_b.vel.invert(); + // } } } fn detect_collisions(&mut self) -> Vec { - let balls = self.balls(); - let players = self.players(); - + // let balls = self.balls(); + // let players = self.players(); + // let mut collisions = vec![]; - for ball in balls.into_iter() { - let collision_opt = players.into_iter().find(|p| p.obj.bounding_box().overlaps(&ball.obj.bounding_box())); - if let None = collision_opt { - continue; - } - let player = collision_opt.unwrap(); - // TODO: This can cause multiple mutable refs of the same player/ball object and therefore does not compile. - collisions.push(Collision {obj_a: player.obj.id, obj_b: ball.obj.id}); - } + // for ball in balls.into_iter() { + // let collision_opt = players.into_iter().find(|p| p.obj.bounding_box().overlaps(&ball.obj.bounding_box())); + // if let None = collision_opt { + // continue; + // } + // let player = collision_opt.unwrap(); + // // TODO: This can cause multiple mutable refs of the same player/ball object and therefore does not compile. + // collisions.push(Collision {obj_a: player.obj.id, obj_b: ball.obj.id}); + // } collisions } pub fn players(&self) -> Vec<&Player> { - self.players().iter().collect() + self.players.iter().collect() } pub fn balls(&self) -> Vec<&Ball> { @@ -384,6 +218,48 @@ impl Field { } } +#[derive(Clone, Debug, PartialEq)] +pub struct Player { + pub obj: GameObject, +} + +impl Player { + pub fn new(id: u16, x: u16, y: u16, field: &Field) -> Player { + Player { + obj: GameObject { + id, + x, + y, + shape: Shape::Rect, + shape_params: vec![field.width / 25, field.height / 5], + vel: Vector::zero(), + is_static: true + }, + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Ball { + pub obj: GameObject, +} + +impl Ball { + pub fn new(id: u16, x: u16, y: u16, field: &Field) -> Ball { + Ball { + obj: GameObject { + id, + x, + y, + shape: Shape::Circle, + shape_params: vec![field.width / 80], + vel: Vector::zero(), + is_static: false + }, + } + } +} + #[derive(Debug)] pub struct Collision { obj_a: u16,