-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgame.cpp
More file actions
executable file
·126 lines (106 loc) · 4.14 KB
/
game.cpp
File metadata and controls
executable file
·126 lines (106 loc) · 4.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include "game.h"
#include "utils.h"
#include "table.h"
#include <QJsonArray>
#include <stdexcept>
#include <cmath>
#include <iostream>
/* how large the window's width should at least be */
int Game::getMinimumWidth() const { return m_table->getWidth(); }
/* how large the window's height should at least be */
int Game::getMinimumHeight() const { return m_table->getHeight(); }
Game::~Game() {
// cleanup ya boi
for (auto b : *m_balls) delete b;
delete m_balls;
delete m_table;
}
void Game::render(QPainter &painter) const {
// table is rendered first, as its the lowest
m_table->render(painter);
// then render all the balls
for (Ball* b : *m_balls) b->render(painter);
}
void Game::animate(double dt) {
// (test) collide the ball with each other ball exactly once
// to achieve this, balls only check collisions with balls "after them"
for (auto it = m_balls->begin(); it != m_balls->end(); ++it) {
Ball* ballA = *it;
// correct ball velocity if colliding with table
// or if stage 2, deletes the ball... omg
// maybe I should have a batchDelete at the end of one animation
// so that I don't get issues with like null pointers
// because ballA may not be a valid pointer after resolveCollision
// is called, and yet I call resolveCollision(ballA, ballB)
// before the thing is over
m_table->resolveCollision(ballA, this);
// check collision with all later balls
for (auto nestedIt = it + 1; nestedIt != m_balls->end(); ++nestedIt) {
Ball* ballB = *nestedIt;
resolveCollision(ballA, ballB);
}
// move ball due to speed
ballA->translate(ballA->getVelocity() * dt);
// apply friction
ballA->changeVelocity(-ballA->getVelocity() * m_table->getFriction() * dt);
}
deleteDeadBalls();
}
/**
* @brief Game::makeDead deletes the given ball pointed to by b from the vector m_balls
* @param b - the ball to remove from the game
*/
void Game::makeDead(Ball *b) {
m_dead_balls->push_back(b);
}
void Game::deleteDeadBalls() {
for (auto it = m_dead_balls->begin(); it != m_dead_balls->end(); ++it) {
// for each dead ball, go through and find a match with
// the one sitting inside the array that's well actually the same
// element
for (auto jt = m_balls->begin(); jt != m_balls->end(); ++jt) {
if (*it == *jt) {
// it is the ball inside dead_balls
// jt is the ball inside m_balls
m_balls->erase(jt);
break; // ie stop looking for that shit
}
}
}
// actually deleting the balls from memory
for (Ball* b : *m_dead_balls) delete b;
m_dead_balls->clear();
}
void Game::resolveCollision(Ball* ballA, Ball* ballB) {
// SOURCE : ASSIGNMENT SPEC
// if not colliding (distance is larger than radii)
QVector2D collisionVector = ballB->getPosition() - ballA->getPosition();
if (collisionVector.length() > ballA->getRadius() + ballB->getRadius()) return;
// at this point, ball B and A definitely collided.
// we need to check just about here whether either of them will break.
collisionVector.normalize();
float mr = ballB->getMass() / ballA->getMass();
double pa = QVector2D::dotProduct(collisionVector, ballA->getVelocity());
double pb = QVector2D::dotProduct(collisionVector, ballB->getVelocity());
if (pa <= 0 && pb >= 0) return;
double a = -(mr + 1);
double b = 2*(mr * pb + pa);
double c = -((mr - 1)*pb*pb + 2*pa*pb);
double disc = sqrt(b*b - 4*a*c);
double root = (-b + disc)/(2*a);
if (root - pb < 0.01) {
root = (-b - disc)/(2*a);
}
QVector2D deltaVa = mr * (pb - root) * collisionVector;
QVector2D deltaVb = (root-pb) * collisionVector;
// now call maybeBreakBall()
// remember to changeVelocity(deltaVa) if it DIDN'T BREAK
if (ballA->maybeBreakBall(deltaVa, m_balls))
makeDead(ballA);
else
ballA->changeVelocity(deltaVa);
if (ballB->maybeBreakBall(deltaVb, m_balls))
makeDead(ballB);
else
ballB->changeVelocity(deltaVb);
}