Skip to content

Commit 57259db

Browse files
committed
refactor(global): abstracted collision mechanism and all the parts with their own structs
1 parent dd946af commit 57259db

File tree

6 files changed

+481
-156
lines changed

6 files changed

+481
-156
lines changed

src/collision.rs

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
use crate::coord::Coord;
2+
3+
fn is_point_on_line(point_coord: &Coord, (start_coord, end_coord): (&Coord, &Coord)) -> bool {
4+
let Coord { x: px, y: py } = point_coord;
5+
let Coord { x: x1, y: y1 } = start_coord;
6+
let Coord { x: x2, y: y2 } = end_coord;
7+
8+
let cross_product = (py - y1) * (x2 - x1) - (px - x1) * (y2 - y1);
9+
if cross_product.abs() > std::f64::EPSILON {
10+
return false;
11+
}
12+
13+
let dot_product = (px - x1) * (x2 - x1) + (py - y1) * (y2 - y1);
14+
if dot_product < 0.0 {
15+
return false;
16+
}
17+
18+
let squared_length = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
19+
if dot_product > squared_length {
20+
return false;
21+
}
22+
23+
true
24+
}
25+
26+
pub fn check_collisions(collidable1: &dyn Collidable, collidable2: &dyn Collidable) -> bool {
27+
let border1 = collidable1.get_border();
28+
let border2 = collidable2.get_border();
29+
30+
// collision between points
31+
if border1.len() == 1 && border2.len() == 1 {
32+
if border1[0].x == border2[0].x && border1[0].y == border2[0].y {
33+
return true;
34+
}
35+
}
36+
// collision between point and line
37+
else if border1.len() == 1 || border2.len() == 1 {
38+
let (point, borders) = if border1.len() == 1 {
39+
(&border1[0], &border2)
40+
} else {
41+
(&border2[0], &border1)
42+
};
43+
44+
for line in borders.windows(2) {
45+
if is_point_on_line(&point, (&line[0], &line[1])) {
46+
return true;
47+
}
48+
}
49+
}
50+
// collision between two lines or shapes
51+
else {
52+
for line1 in border1.windows(2) {
53+
for line2 in border2.windows(2) {
54+
let x1 = line1[0].x;
55+
let y1 = line1[0].y;
56+
let x2 = line1[1].x;
57+
let y2 = line1[1].y;
58+
59+
let x3 = line2[0].x;
60+
let y3 = line2[0].y;
61+
let x4 = line2[1].x;
62+
let y4 = line2[1].y;
63+
64+
let den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
65+
66+
if den == 0.0 {
67+
continue;
68+
}
69+
70+
let t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / den;
71+
let u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den;
72+
73+
if t >= 0.0 && t <= 1.0 && u >= 0.0 && u <= 1.0 {
74+
return true;
75+
}
76+
}
77+
}
78+
}
79+
80+
false
81+
}
82+
83+
pub trait Collidable {
84+
fn get_border(&self) -> Vec<Coord>;
85+
}
86+
87+
#[cfg(test)]
88+
mod tests {
89+
use super::*;
90+
91+
struct TestCollidable {
92+
border: Vec<Coord>,
93+
}
94+
95+
impl Collidable for TestCollidable {
96+
fn get_border(&self) -> Vec<Coord> {
97+
self.border.clone()
98+
}
99+
}
100+
101+
#[test]
102+
fn test_collision_manager() {
103+
let collidable1 = TestCollidable {
104+
border: vec![
105+
Coord::new(0.0, 0.0),
106+
Coord::new(0.0, 5.0),
107+
Coord::new(5.0, 5.0),
108+
Coord::new(5.0, 0.0),
109+
],
110+
};
111+
112+
let collidable2 = TestCollidable {
113+
border: vec![
114+
Coord::new(0.0, 5.0),
115+
Coord::new(1.5, 0.0),
116+
Coord::new(2.5, 2.5),
117+
Coord::new(3.5, 0.0),
118+
Coord::new(5.0, 5.0),
119+
],
120+
};
121+
122+
let collided = check_collisions(&collidable1, &collidable2);
123+
124+
assert_eq!(collided, true);
125+
}
126+
127+
#[test]
128+
fn test_no_collision() {
129+
let collidable1 = TestCollidable {
130+
border: vec![
131+
Coord::new(0.0, 0.0),
132+
Coord::new(0.0, 1.0),
133+
Coord::new(1.0, 1.0),
134+
Coord::new(1.0, 0.0),
135+
],
136+
};
137+
138+
let collidable2 = TestCollidable {
139+
border: vec![
140+
Coord::new(2.0, 0.0),
141+
Coord::new(3.0, 0.0),
142+
Coord::new(2.0, 1.0),
143+
Coord::new(3.0, 1.0),
144+
],
145+
};
146+
147+
let collided = check_collisions(&collidable1, &collidable2);
148+
149+
assert_eq!(collided, false);
150+
}
151+
152+
#[test]
153+
fn test_no_collision_with_same_border() {
154+
let collidable1 = TestCollidable {
155+
border: vec![
156+
Coord::new(0.0, 0.0),
157+
Coord::new(0.0, 1.0),
158+
Coord::new(1.0, 1.0),
159+
Coord::new(1.0, 0.0),
160+
],
161+
};
162+
163+
let collidable2 = TestCollidable {
164+
border: vec![Coord::new(0.0, 0.0)],
165+
};
166+
167+
let collided = check_collisions(&collidable1, &collidable2);
168+
169+
assert_eq!(collided, true);
170+
}
171+
172+
#[test]
173+
fn test_collision_of_two_points() {
174+
let collidable1 = TestCollidable {
175+
border: vec![Coord::new(0.0, 0.0)],
176+
};
177+
178+
let collidable2 = TestCollidable {
179+
border: vec![Coord::new(0.0, 0.0)],
180+
};
181+
182+
let collided = check_collisions(&collidable1, &collidable2);
183+
184+
assert_eq!(collided, true);
185+
}
186+
187+
#[test]
188+
fn test_collision_of_points_over_a_line() {
189+
let collidable1 = TestCollidable {
190+
border: vec![Coord::new(0.0, 0.0), Coord::new(9.0, 0.0)],
191+
};
192+
193+
let collidable2 = TestCollidable {
194+
border: vec![Coord::new(5.0, 0.0)],
195+
};
196+
197+
let collided = check_collisions(&collidable1, &collidable2);
198+
199+
assert_eq!(collided, true);
200+
}
201+
}

src/game.rs

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use crate::coord::Coord;
2-
31
const SPEED: u8 = 8;
42

53
#[derive(PartialEq)]
@@ -20,12 +18,7 @@ pub enum GameState {
2018

2119
pub struct Game {
2220
pub score: i32,
23-
pub total_length: i32,
2421
pub state: GameState,
25-
pub corners: Vec<Coord>,
26-
pub head_coord: Coord,
27-
pub direction: Direction,
28-
pub point_coord: Option<Coord>,
2922
pub frame_num: i32,
3023
speed: u8,
3124
}
@@ -35,19 +28,13 @@ impl Game {
3528
Game {
3629
score: 0,
3730
state: GameState::Startup,
38-
corners: Vec::from([Coord::new(-12.0, 10.0)]),
39-
total_length: 12,
40-
head_coord: Coord::new(0.0, 10.0),
41-
direction: Direction::Right,
42-
point_coord: None,
4331
frame_num: 0,
4432
speed: SPEED,
4533
}
4634
}
4735

4836
pub fn increase_score(&mut self) {
4937
self.score += 1;
50-
self.total_length += 2;
5138
}
5239

5340
pub fn increase_frame_num(&mut self) {
@@ -60,71 +47,13 @@ impl Game {
6047
};
6148
}
6249

63-
pub fn move_head(&mut self) {
64-
match self.direction {
65-
Direction::Up => self.head_coord.move_up(1.0),
66-
Direction::Down => self.head_coord.move_down(1.0),
67-
Direction::Left => self.head_coord.move_left(1.0),
68-
Direction::Right => self.head_coord.move_right(1.0),
69-
}
70-
}
71-
7250
pub fn game_over(&mut self) {
7351
self.state = GameState::GameOver;
7452
}
7553

76-
pub fn move_tail(&mut self) {
77-
let tail_coord = &self.corners[0];
78-
79-
if self.corners.len() == 1 {
80-
let tail_distance_x = Coord::get_x_distance(tail_coord, &self.head_coord);
81-
let tail_distance_y = Coord::get_y_distance(tail_coord, &self.head_coord);
82-
83-
if tail_distance_x + tail_distance_y > (self.total_length as f64) {
84-
self.corners[0].move_toward(&self.head_coord);
85-
}
86-
} else {
87-
let next_corner = self.corners[1].clone();
88-
self.corners[0].move_toward(&next_corner);
89-
90-
if self.corners[0].compare(&next_corner) {
91-
self.corners.remove(0);
92-
}
93-
}
94-
}
95-
96-
pub fn change_direction(&mut self, direction: Direction) {
97-
self.direction = direction;
98-
self.corners
99-
.push(Coord::new(self.head_coord.x, self.head_coord.y))
100-
}
101-
10254
pub fn restart(&mut self) {
10355
self.score = 0;
104-
self.total_length = 12;
10556
self.state = GameState::Running;
106-
self.corners = Vec::from([Coord::new(-12.0, 10.0)]);
107-
self.head_coord = Coord::new(0.0, 10.0);
108-
self.direction = Direction::Right;
109-
self.point_coord = None;
11057
self.frame_num = 0;
11158
}
112-
113-
pub fn generate_new_point(&mut self, width: f64, height: f64) {
114-
self.point_coord = Some(Coord::randomize(width - 1.0, height - 1.0));
115-
}
116-
117-
pub fn check_beat(&mut self) {
118-
self.corners.windows(2).for_each(|pair| {
119-
let start = &pair[0];
120-
let end = &pair[1];
121-
122-
// check if the head coord is between start and end
123-
if (self.head_coord.x - start.x) * (self.head_coord.x - end.x) <= 0.0
124-
&& (self.head_coord.y - start.y) * (self.head_coord.y - end.y) <= 0.0
125-
{
126-
self.state = GameState::GameOver;
127-
}
128-
});
129-
}
13059
}

0 commit comments

Comments
 (0)