C++ Project 2016-2017: Arkanoid
ball.cpp
Go to the documentation of this file.
1 
3 #include "ball.h"
4 #include "../wall/wall.h"
5 #include "../block/block.h"
6 #include "../../math/vector2D.h"
7 
8 #include <iostream>
9 #include <vector>
10 #include <cmath>
11 #include <memory>
12 
13 using namespace std;
14 
15 namespace arkanoid {
16 
17  Ball::Ball() : notMoving(true), random(Random::getInstance()) {}
18 
19  Ball::Ball(double x, double y, double newSpeed, pair<double, double> size) : velocity(newSpeed, -newSpeed), origin(x,y), speed(newSpeed), notMoving(true), spedUp(false), random(Random::getInstance()), Entity(x, y, size) {}
20 
22 
23  void Ball::update() {}
24 
25  void Ball::draw() const {}
26 
27  void Ball::speedUp(double factor) {
28  if(!spedUp) {
29  velocity *= factor;
30  spedUp = true;
31  }
32  }
33 
34  void Ball::setInvisible(int period) {
35  invisDuration = period;
36  }
37 
38  template<typename T>
39  vector<int> Ball::bounceIfPossible(vector<unique_ptr<T>> const &entities) {
40 
41  // Check for every Entity if there's a collision
42  vector<int> collisions; // At most 2 collisions
43  for(int e = 0; e < entities.size(); e++) {
44  if(collidesWith(*entities[e])) {
45  collisions.push_back(e);
46  }
47  }
48 
49  if(!collisions.empty()) {
50  if(collisions.size() == 2) {
51  // Use procedure for two Entities:
52  // If two collided Entities are on top of each other:
53  // -> Ball was coming from left/right
54  // Oterwise: Ball was coming from top/left
55  if(entities[collisions[0]]->getPosition().x == entities[collisions[1]]->getPosition().x) {
56  // Ball coming from left/right
57  velocity.x *= -1;
58  } else if(entities[collisions[0]]->getPosition().y == entities[collisions[1]]->getPosition().y) {
59  // Ball coming from top/bottom
60  velocity.y *= -1;
61  } else {
62  // EDGE CASE
63  velocity *= -1;
64  }
65  } else if(collisions.size() == 1) {
66  // Use procedure for one Entity
67 
68  // Calculate the gaps between the Ball and the collided Entity on all sides
69  const double gapRight = abs((position.x + size.first) - entities[collisions[0]]->getPosition().x);
70  const double gapLeft = abs(position.x - (entities[collisions[0]]->getPosition().x + entities[collisions[0]]->getSize().first));
71  const double gapTop = abs(position.y - (entities[collisions[0]]->getPosition().y + entities[collisions[0]]->getSize().second));
72  const double gapBottom = abs((position.y + size.second) - entities[collisions[0]]->getPosition().y);
73 
74  // Determine the smallest gaps, both horiztontally and vertically
75  const double minGapHor = min(gapRight, gapLeft);
76  const double minGapVer = min(gapTop, gapBottom);
77 
78  // Change the Ball 's direction based on the calculation above
79  if(minGapHor < minGapVer) {
80  // Ball coming from left/right
81  velocity.x *= -1;
82  } else {
83  // Ball coming from top/bottom
84  velocity.y *= -1;
85  }
86  } else {
87  // 3 collisions
88  velocity *= 1;
89  }
90  }
91 
92  return collisions;
93  }
94 
95  // Prevent linker errors
96  template vector<int> Ball::bounceIfPossible<Block>(vector<unique_ptr<Block>> const &entities);
97  template vector<int> Ball::bounceIfPossible<Wall>(vector<unique_ptr<Wall>> const &entities);
98 
99  void Ball::bounceIfPossible(unique_ptr<Player> const &player) {
100 
101  if(collidesWith(*player) && !notMoving) {
102 
103  const double maxPlayerPos = player->getPosition().x + player->getSize().first;
104  const double playerLength = maxPlayerPos - player->getPosition().x;
105  const double halfLength = playerLength / 2;
106  const double centerPlayer = maxPlayerPos - (playerLength / 2);
107 
108  double collisionPoint = position.x + (size.first / 2);
109  // Check if Ball is not outside the Players surface
110  if(collisionPoint > maxPlayerPos) {
111  collisionPoint = maxPlayerPos;
112  } else if(collisionPoint < player->getPosition().x) {
113  collisionPoint = player->getPosition().x;
114  }
115  const double relativeCollision = collisionPoint - player->getPosition().x;
116 
117  double ratio = 0.0; // how much the Ball is located towards the edge
118  double angle = 0.0;
119  if(collisionPoint > centerPlayer) {
120  // right half of the player
121  ratio = (relativeCollision - halfLength) / halfLength;
122  angle = 90.0 - (ratio * 70.0); // maximum angle = 70°
123 
124  } else {
125  // left half of the player
126  ratio = (halfLength - relativeCollision) / halfLength;
127  angle = 90.0 + (ratio * 70.0); // maximum angle = 70°
128 
129  }
130 
131  velocity.x = speed * cos((angle + random->randomDouble(-5, 5)) * (M_PI/180));
132  velocity.y = -1 * speed * sin((angle + random->randomDouble(-5, 5)) * (M_PI/180));
133 
134  // Reset flags (from hitting a block)
135  spedUp = false;
136 
137  }
138  }
139 
140  void Ball::reset() {
141  position = origin;
142  velocity.x = speed;
143  velocity.y = -speed;
144  notMoving = true;
145  invisDuration = 0;
146  spedUp = false;
147  }
148 
149 }
void draw() const
Definition: ball.cpp:25
Vector2D position
The current position of the Entity (in the 9x7 grid).
Definition: entity.h:17
void update()
Definition: ball.cpp:23
bool collidesWith(const Entity &other) const
Definition: entity.cpp:38
virtual void reset()
Definition: ball.cpp:140
Vector2D getPosition() const
Definition: entity.cpp:17
vector< int > bounceIfPossible(vector< unique_ptr< T >> const &entities)
Definition: ball.cpp:39
double randomDouble(int from, int to)
Definition: random.cpp:28
void speedUp(double factor)
Definition: ball.cpp:27
double y
The &#39;y&#39; component of the Vector2D.
Definition: vector2D.h:18
Singleton: generates "random" numbers.
Definition: random.h:14
double x
The &#39;x&#39; component of the Vector2D.
Definition: vector2D.h:17
All the game logic of the Arkanoid game.
Definition: ball.cpp:15
An Entity represents an "object" (like Player, Ball, Block, ...) in the game World.
Definition: entity.h:15
void setInvisible(int period)
Definition: ball.cpp:34
pair< double, double > size
The size (width and height respectively) of the Entity.
Definition: entity.h:18