block_breaker/
ball.rs

1use crate::dimensions::Dimensions;
2use crate::position::Position;
3use crate::vector::Vector;
4
5/// The ball
6pub struct Ball {
7    position: Position,
8    velocity: Vector,
9    power: u16,
10    game_dimensions: Dimensions,
11}
12
13impl Ball {
14    /// Create a new ball
15    ///
16    /// The power of a ball is the amount of damage it does to blocks when it collides
17    pub fn new(position: Position, game_dimensions: Dimensions, power: u16) -> Ball {
18        Ball {
19            position,
20            velocity: Vector::new(0., 1.),
21            power,
22            game_dimensions,
23        }
24    }
25
26    /// Get the x coordinate of the ball
27    pub fn x(&self) -> u16 {
28        self.position.x()
29    }
30
31    /// Get the y coordinate of the ball
32    pub fn y(&self) -> u16 {
33        self.position.y()
34    }
35
36    /// Get the power of the ball
37    pub fn power(&self) -> u16 {
38        self.power
39    }
40
41    /// Update the game dimensions, this moves the ball back into view if it wouldn't be in the new dimensions
42    pub fn update_dimensions(&mut self, dimensions: Dimensions) {
43        self.game_dimensions = dimensions;
44        if self.x() >= self.game_dimensions.width() {
45            self.position = Position::new(self.game_dimensions.width() - 1, self.y())
46        }
47        if self.y() >= self.game_dimensions.height() {
48            self.position = Position::new(self.x(), self.game_dimensions.height() - 1)
49        }
50    }
51
52    /// Make the ball take a step
53    pub fn tick(&mut self) {
54        let mut new_position = &self.position + &self.velocity;
55
56        if new_position.x() >= self.game_dimensions.width() {
57            new_position = Position::new(self.game_dimensions.width() - 1, new_position.y())
58        }
59        if new_position.y() >= self.game_dimensions.height() {
60            new_position = Position::new(new_position.x(), self.game_dimensions.height())
61        }
62        if new_position.y() >= self.game_dimensions.height() - 1 {
63            self.velocity = Vector::new(0., 0.)
64        }
65        self.position = new_position
66    }
67
68    /// Bounce the ball off in a normal
69    pub fn bounce(&mut self, normal: Vector) {
70        self.velocity -= 2. * self.velocity.dot(&normal) * normal;
71        self.velocity.normalise()
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn bounce_works() {
81        let mut ball = Ball::new(Position::new(0, 0), Dimensions::new(100, 100), 1);
82
83        // ball is initially travelling directly down
84        // bouncing in (0, -1) should make it's velocity (0, -1)
85        ball.bounce(Vector::new(0., -1.));
86        assert_eq!(ball.velocity, Vector::new(0., -1.));
87    }
88}