No Description

Breakout.cpp 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. //
  2. // Breakout.cpp
  3. // ogamalBreakout
  4. //
  5. // Created by Osama Attia on 9/21/14.
  6. // ogamal@iastate.edu
  7. //
  8. #include "Breakout.h"
  9. #include <stdio.h>
  10. using namespace std;
  11. void recomputeFrame(int value);
  12. Breakout::Breakout() {
  13. init();
  14. }
  15. Breakout::~Breakout() {
  16. }
  17. void Breakout::display(void) {
  18. // Clear buffer
  19. glClear(GL_COLOR_BUFFER_BIT);
  20. // Set OpenGL for 2D drawing
  21. glMatrixMode(GL_PROJECTION);
  22. glLoadIdentity();
  23. glDisable(GL_LIGHTING);
  24. glDisable(GL_DEPTH_TEST);
  25. glDisable(GL_TEXTURE_2D);
  26. glOrtho(0.0f, WINWIDTH, WINHEIGHT, 0.0f, 0.0f, 1.0f);
  27. glMatrixMode(GL_MODELVIEW);
  28. glLoadIdentity();
  29. // Draw my cool gradient background
  30. drawBackground();
  31. // Select which state of the game to display
  32. switch (gameState) {
  33. case INIT:
  34. // Init values
  35. init();
  36. break;
  37. case Menus:
  38. // TODO List menu
  39. break;
  40. case Gameplay:
  41. // Draw the game
  42. drawGame();
  43. // If no balls, player loses the game
  44. if (balls.size() <= 0 & lifesCount > 0) {
  45. newBall(-1, -1);
  46. lifesCount--;
  47. reward = 100;
  48. } else if (balls.size() <= 0) {
  49. // TODO - GAME OVER
  50. }
  51. // If no bricks, player wins the level
  52. if (bricks.size() <= 0 && level <= 2) {
  53. level++;
  54. initBricks();
  55. } else if (bricks.size() <= 0) {
  56. // TODO - PLAYER WON
  57. }
  58. break;
  59. case Scoreboard:
  60. // TODO
  61. break;
  62. default:
  63. break;
  64. }
  65. glutTimerFunc(TIMER, recomputeFrame, 0);
  66. glutSwapBuffers();
  67. }
  68. void recomputeFrame(int value) {
  69. glutPostRedisplay();
  70. }
  71. void Breakout::init(void) {
  72. // Reset game statistics
  73. score = 0;
  74. level = 1;
  75. reward = 100;
  76. lifesCount = 3;
  77. // Remove all balls
  78. balls.clear();
  79. // Remove all bricks
  80. bricks.clear();
  81. // Init bricks
  82. initBricks();
  83. // Add ball and paddle
  84. initPaddle();
  85. newBall(-1, -1);
  86. // Start game play
  87. gameState = Breakout::Gameplay;
  88. }
  89. void Breakout::drawBackground(void) {
  90. glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  91. glBegin(GL_QUADS);
  92. // Top color
  93. glColor3f(0.3f, 0.3f, 0.3f);
  94. glVertex2f(WINWIDTH, WINHEIGHT);
  95. glVertex2f(-WINWIDTH, WINHEIGHT);
  96. // Bottom color
  97. glColor3f(0.0f, 0.0f, 0.0f);
  98. glVertex2f(0.0f, 0.0f);
  99. glVertex2f(0.0f, 0.0f);
  100. glEnd();
  101. }
  102. void Breakout::drawGame(void) {
  103. // // Draw coordinates for guidance
  104. // drawCoordinate();
  105. // Draw balls
  106. drawBalls();
  107. // Draw bricks
  108. drawBricks();
  109. // Draw paddle
  110. drawPaddle();
  111. // Draw game statistics (lifes, score)
  112. drawGameStats();
  113. }
  114. void Breakout::newBall(float x = -1, float y = -1) {
  115. Ball b1;
  116. if (x < 0 || y < 0) {
  117. b1.xpos = WINWIDTH / 2.0;
  118. b1.ypos = WINHEIGHT - 30.0f;
  119. } else {
  120. b1.xpos = x;
  121. b1.ypos = y;
  122. }
  123. if ((float) rand() / (RAND_MAX) < 0.5)
  124. b1.xvel = 5.0f;
  125. else
  126. b1.xvel = -5.0f;
  127. b1.yvel = -10.0f;
  128. b1.radius = BALL_RADIUS;
  129. b1.r = 0.4f + (float) rand() / (RAND_MAX);
  130. b1.g = 0.25f + (float) rand() / (RAND_MAX);
  131. b1.b = 0.4f + (float) rand() / (RAND_MAX);
  132. balls.push_back(b1);
  133. }
  134. void Breakout::drawBalls(void) {
  135. for (std::vector<Ball>::iterator it = balls.begin(); it != balls.end(); ) {
  136. glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // use GL_LINE if no fill
  137. glBegin(GL_POLYGON);
  138. glColor3f(it->r, it->g, it->b);
  139. for(int j = 0; j < CIRCLE_SEGMENTS; j++) {
  140. float const theta = 2.0f * 3.1415926f * (float)j / (float)CIRCLE_SEGMENTS;
  141. float const x = it->radius * cosf(theta);
  142. float const y = it->radius * sinf(theta);
  143. glVertex2f(x + it->xpos, y + it->ypos);
  144. }
  145. glEnd();
  146. // Set new position
  147. it->xpos += it->xvel;
  148. it->ypos += it->yvel;
  149. // Collision with left/right/top window sides
  150. if ( (it->xpos <= (2 * it->radius)) || (it ->xpos >= (WINWIDTH - 2 * it->radius)) ) {
  151. it->xvel *= -1;
  152. }
  153. if ( (it->ypos <= (2 * it->radius)) ) {
  154. it->yvel *= -1;
  155. }
  156. if (it->ypos >= (WINHEIGHT - 2 * it->radius)) {
  157. it = balls.erase(it);
  158. continue;
  159. }
  160. // Collission with the bricks
  161. for (std::vector<Brick>::iterator br = bricks.begin(); br != bricks.end(); ) {
  162. // Check collission between circle and vertical brick sides
  163. if (it->ypos >= br->ypos && it->ypos <= br->ypos + br->height) {
  164. // brick right edge and left point on circle
  165. if ((it->xpos - it->radius - br->xpos - br->width) <= 5 && (it->xpos - it->radius - br->xpos - br->width) >= 0) {
  166. it->xvel *= -1;
  167. br = hitBrick(br);
  168. continue;
  169. }
  170. // brick left edge and right point on circle
  171. if ((it->xpos + it->radius - br->xpos) >= -5 && (it->xpos + it->radius - br->xpos) <= 0) {
  172. it->xvel *= -1;
  173. br = hitBrick(br);
  174. continue;
  175. }
  176. }
  177. // Check collission between circle and horizontal brick sides
  178. if (it->xpos >= br->xpos && it->xpos <= br->xpos + br->width) {
  179. // brick bottom edge and top point on circle
  180. if ((it->ypos - it->radius - br->ypos - br->height) <= 10 && (it->ypos - it->radius - br->ypos - br->height) >= 0) {
  181. it->yvel *= -1;
  182. br = hitBrick(br);
  183. continue;
  184. }
  185. // brick top edge and bottom point on circle
  186. if ((it->ypos + it->radius - br->ypos) >= -10 && (it->ypos + it->radius - br->ypos) <= 0) {
  187. it->yvel *= -1;
  188. br = hitBrick(br);
  189. continue;
  190. }
  191. }
  192. GLfloat d;
  193. // Check collission with top left corner
  194. d = pow((it->xpos - br->xpos), 2.0) + pow((it->ypos - br->ypos), 2.0);
  195. if (d < it->radius + 5.0) {
  196. it->xvel *= -1;
  197. it->yvel *= -1;
  198. br = hitBrick(br);
  199. continue;
  200. }
  201. // Check collission with top right corner
  202. d = pow((it->xpos - br->xpos - br->width), 2.0) + pow((it->ypos - br->ypos), 2.0);
  203. if (d < it->radius + 5.0) {
  204. it->xvel *= -1;
  205. it->yvel *= -1;
  206. br = hitBrick(br);
  207. continue;
  208. }
  209. // Check collission with bottom left corner
  210. d = pow((it->xpos - br->xpos), 2.0) + pow((it->ypos - br->ypos - br->height), 2.0);
  211. if (d < it->radius + 5.0) {
  212. it->xvel *= -1;
  213. it->yvel *= -1;
  214. br = hitBrick(br);
  215. continue;
  216. }
  217. // Check collission with bottom right corner
  218. d = pow((it->xpos - br->xpos - br->width), 2.0) + pow((it->ypos - br->ypos - br->height), 2.0);
  219. if (d < it->radius + 5.0) {
  220. it->xvel *= -1;
  221. it->yvel *= -1;
  222. br = hitBrick(br);
  223. continue;
  224. }
  225. ++br; // next brick
  226. }
  227. // Check collission between paddle's top edge and bottom point on circle
  228. if (it->xpos >= paddle.xpos && it->xpos <= paddle.xpos + paddle.width) {
  229. if ((it->ypos + it->radius - paddle.ypos) >= -10 && (it->ypos + it->radius - paddle.ypos) <= 0) {
  230. it->yvel *= -1;
  231. reward = 100;
  232. score += reward;
  233. continue;
  234. }
  235. }
  236. ++it; // next ball
  237. }
  238. }
  239. void Breakout::initPaddle(void) {
  240. paddle.r = 0.2f;
  241. paddle.g = 0.5f;
  242. paddle.b = 1.0f;
  243. paddle.width = 150.0f;
  244. paddle.height = 12.0f;
  245. paddle.xpos = WINWIDTH / 2.0f - paddle.width / 2.0f;
  246. paddle.ypos = WINHEIGHT - 20.0f;
  247. }
  248. void Breakout::drawPaddle() {
  249. // Make sure paddle is larger than 25px
  250. if (paddle.width < 25) {
  251. paddle.width = 25;
  252. }
  253. glColor3f(paddle.r, paddle.g, paddle.b);
  254. glRectf(paddle.xpos, paddle.ypos, paddle.xpos + 5.0f, paddle.ypos + paddle.height);
  255. glRectf(paddle.xpos + 10.0f, paddle.ypos, paddle.xpos + paddle.width - 10.0f, paddle.ypos + paddle.height);
  256. glRectf(paddle.xpos + paddle.width - 5.0f, paddle.ypos, paddle.xpos + paddle.width, paddle.ypos + paddle.height);
  257. }
  258. void Breakout::drawBricks(void) {
  259. for (std::vector<Brick>::iterator it = bricks.begin(); it != bricks.end(); ++it) {
  260. glColor3f(it->r, it->g, it->b);
  261. glRectf(it->xpos, it->ypos, it->xpos + it->width, it->ypos + it->height);
  262. // Top cool triangle (kind of texture)
  263. glBegin(GL_QUADS);
  264. glColor3f(it->r-0.2f, it->g-0.2f, it->b-0.2f);
  265. glVertex2f(it->xpos, it->ypos);
  266. glColor3f(it->r-0.05f, it->g-0.05f, it->b-0.05f);
  267. glVertex2f(it->xpos + it->width, it->ypos);
  268. glColor3f(it->r-0.15f, it->g-0.15f, it->b-0.15f);
  269. glVertex2f(it->xpos + it->width, it->ypos + it->height);
  270. glVertex2f(it->xpos, it->ypos);
  271. glEnd();
  272. }
  273. }
  274. template <typename Iterator>
  275. Iterator Breakout::hitBrick(Iterator brick) {
  276. score += reward;
  277. reward += 25;
  278. // system("afpqlay ../../cartoon008.wav");
  279. // Decrease brick health
  280. if (brick->health > 1) {
  281. brick->r = 0.95f;
  282. brick->g = 0.95f;
  283. brick->b = 0.95f;
  284. brick->health -= 1;
  285. return ++brick;
  286. } else {
  287. return bricks.erase(brick);
  288. }
  289. }
  290. void Breakout::initBricks(void) {
  291. if (level == 1)
  292. bricksLevel1();
  293. else if (level == 2)
  294. bricksLevel2();
  295. }
  296. void Breakout::bricksLevel1(void) {
  297. Brick newBrick;
  298. newBrick.r = 0.95f;
  299. newBrick.g = 0.95f;
  300. newBrick.b = 0.95f;
  301. newBrick.health = 1;
  302. newBrick.width = (WALLWIDTH - (WALLCOLS - 2) * WALLSPACE) / WALLCOLS;
  303. newBrick.height = (WALLHEIGHT - (WALLROWS - 2) * WALLSPACE) / WALLROWS;
  304. for (int i = 0; i < WALLROWS; ++i) {
  305. for (int j = 0; j < WALLCOLS; ++j) {
  306. // Set stronger bricks
  307. if (i+1 > ceil(WALLROWS / 2.0) - 2 && i < ceil(WALLROWS / 2.0) + 2 && j+2 > ceil(WALLCOLS / 2.0) - 3 && j < ceil(WALLCOLS / 2.0) + 3) {
  308. newBrick.r = 1.0f;
  309. newBrick.g = 0.5f;
  310. newBrick.b = 0.5f;
  311. newBrick.health = 2;
  312. } else {
  313. newBrick.r = 0.95f;
  314. newBrick.g = 0.95f;
  315. newBrick.b = 0.95f;
  316. newBrick.health = 1;
  317. }
  318. newBrick.xpos = WALLX + j * newBrick.width + j * WALLSPACE;
  319. newBrick.ypos = WALLY + i * newBrick.height + i * WALLSPACE;
  320. bricks.push_back(newBrick);
  321. }
  322. }
  323. }
  324. void Breakout::bricksLevel2(void) {
  325. Brick newBrick;
  326. newBrick.width = (WALLWIDTH - (WALLCOLS - 2) * WALLSPACE) / WALLCOLS;
  327. newBrick.height = (WALLHEIGHT - (WALLROWS - 2) * WALLSPACE) / WALLROWS;
  328. for (int i = 0; i < WALLROWS; i++) {
  329. for (int j = 0; j < WALLCOLS; j++) {
  330. // Set stronger bricks
  331. if (i == 1 || i == WALLROWS - 2 || j == 1 || j == WALLCOLS - 2) {
  332. newBrick.r = 1.0f;
  333. newBrick.g = 0.5f;
  334. newBrick.b = 0.5f;
  335. newBrick.health = 2;
  336. } else {
  337. newBrick.r = 0.95f;
  338. newBrick.g = 0.95f;
  339. newBrick.b = 0.95f;
  340. newBrick.health = 1;
  341. }
  342. newBrick.xpos = WALLX + j * newBrick.width + j * WALLSPACE;
  343. newBrick.ypos = WALLY + i * newBrick.height + i * WALLSPACE;
  344. bricks.push_back(newBrick);
  345. }
  346. }
  347. }
  348. void Breakout::drawGameStats(void) {
  349. glBegin(GL_LINES);
  350. // Bottom right (red)
  351. glColor3f(1.0f, 0.0f, 0.0f);
  352. glVertex2f(20.0f, 30.0f);
  353. glVertex2f(WINWIDTH - 20.0f, 30.0f);
  354. glEnd();
  355. float offset = 25.0f;
  356. for (int i = 0; i < lifesCount & i < 10; ++i) {
  357. drawLife(35 + offset * i, 15);
  358. }
  359. drawScore();
  360. }
  361. void Breakout::drawLife(float x, float y) {
  362. // Scale the heart symbol
  363. float const scale = 0.5f;
  364. // Heart symbol equations from Walfram Mathworld: http://mathworld.wolfram.com/HeartCurve.html
  365. glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  366. glBegin(GL_POLYGON);
  367. glColor3f(1.0f, 0.2f, 0.2f);
  368. for(int j = 0; j < CIRCLE_SEGMENTS; j++) {
  369. float const theta = 2.0f * 3.1415926f * (float)j / (float)CIRCLE_SEGMENTS;
  370. float const xx = scale * 16.0f * sinf(theta) * sinf(theta) * sinf(theta);
  371. float const yy = -1 * scale * (13.0f * cosf(theta) - 5.0f * cosf(2.0f * theta) - 2 * cosf(3.0f * theta) - cosf(4.0f * theta));
  372. glVertex2f(x + xx, y + yy);
  373. }
  374. glEnd();
  375. }
  376. void Breakout::drawScore(void) {
  377. glPushMatrix();
  378. // Write score word
  379. glColor3f(1.0f, 0.7f, 0.7f);
  380. glRasterPos2f(WINWIDTH - 120, 20);
  381. char buf[300], *p;
  382. p = buf;
  383. sprintf(buf, "Score: ");
  384. do glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *p); while(*(++p));
  385. // Print the score
  386. p = buf;
  387. sprintf(buf, " %d", score);
  388. glColor3f(1.0f, 0.2f, 0.2f);
  389. glRasterPos2f(WINWIDTH - 120, 20);
  390. do glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *p); while(*(++p));
  391. glPopMatrix();
  392. }
  393. void Breakout::drawCoordinate(void) {
  394. glBegin(GL_LINES);
  395. // Top left (white)
  396. glColor3f(1.0f, 1.0f, 1.0f);
  397. glVertex2f(20.0f, 10.0f);
  398. glVertex2f(20.0f, 30.0f);
  399. glVertex2f(10.0f, 20.0f);
  400. glVertex2f(30.0f, 20.0f);
  401. // Bottom right (red)
  402. glColor3f(1.0f, 0.0f, 0.0f);
  403. glVertex2f(WINWIDTH - 20.0f, WINHEIGHT - 10.0f);
  404. glVertex2f(WINWIDTH - 20.0f, WINHEIGHT - 30.0f);
  405. glVertex2f(WINWIDTH - 10.0f, WINHEIGHT - 20.0f);
  406. glVertex2f(WINWIDTH - 30.0f, WINHEIGHT - 20.0f);
  407. glEnd();
  408. }
  409. void Breakout::reshape(int width, int height) {
  410. if (width != WINWIDTH || height != WINHEIGHT)
  411. glutReshapeWindow(WINWIDTH, WINHEIGHT);
  412. }
  413. void Breakout::mouseClick(int button, int state, int x, int y) {
  414. if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
  415. newBall(x, y);
  416. }
  417. // Force redraw
  418. glutPostRedisplay();
  419. }
  420. void Breakout::mouseMove(int x, int y) {
  421. y = WINHEIGHT - y;
  422. if (x - paddle.width / 2.0f >= 0 && x + paddle.width / 2.0f <= WINWIDTH) {
  423. paddle.xpos = x - paddle.width / 2.0f;
  424. } else if (x - paddle.width / 2.0f <= 0) {
  425. paddle.xpos = 0;
  426. } else if (x + paddle.width / 2.0f >= WINWIDTH) {
  427. paddle.xpos = WINWIDTH - paddle.width;
  428. }
  429. glutPostRedisplay();
  430. }
  431. void Breakout::keyStroke(unsigned char key, int x, int y) {
  432. switch (key) {
  433. case 'q': // Exit
  434. exit(0);
  435. break;
  436. case 'n': // New game
  437. init();
  438. break;
  439. case 'h':
  440. lifesCount++;
  441. break;
  442. case 27: // Esc button
  443. exit(0);
  444. break;
  445. default:
  446. break;
  447. }
  448. }
  449. void Breakout::specialKeyPos(int key, int x, int y) {
  450. switch(key)
  451. {
  452. case GLUT_KEY_LEFT:
  453. if (paddle.xpos > 0) {
  454. paddle.xpos -= 5.0f;
  455. paddle.xpos -= 5.0f;
  456. glutPostRedisplay();
  457. paddle.xpos -= 5.0f;
  458. paddle.xpos -= 5.0f;
  459. glutPostRedisplay();
  460. }
  461. break;
  462. case GLUT_KEY_RIGHT:
  463. if (paddle.xpos + paddle.width < WINWIDTH) {
  464. paddle.xpos += 5.0f;
  465. paddle.xpos += 5.0f;
  466. glutPostRedisplay();
  467. paddle.xpos += 5.0f;
  468. paddle.xpos += 5.0f;
  469. glutPostRedisplay();
  470. }
  471. break;
  472. default:
  473. break;
  474. }
  475. }