Balls & Paddle game for RETRO Pong inspired game featuring multi-directional tilt-sensitive paddle, multiple balls, shrinking ceiling and a bit of gravity.
Dependencies: LCD_ST7735 MusicEngine RETRO_BallsAndThings mbed
Balls and Paddle
After doing some work on the Pong mod I decided to put my efforts into making my version object oriented and try to make a generic object-library that could be use for other ball-and-things games. To add some challenges to the gameplay, the following features were added:
- extra-free additional balls to please the juglers
- gravity for pulling the ball down to create some dynamic movement
- directional power-paddle that counters the ball with a bit more speed
- lowering ceiling to make endless gameplay impossible
Game.cpp@4:8643d2d93a5f, 2015-02-26 (annotated)
- Committer:
- maxint
- Date:
- Thu Feb 26 11:55:13 2015 +0000
- Revision:
- 4:8643d2d93a5f
- Parent:
- 3:a68d4d515c20
- Child:
- 5:8441b390a15f
using line collision for paddle
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
maxint | 0:7e989d0083ff | 1 | #include "Game.h" |
maxint | 0:7e989d0083ff | 2 | |
maxint | 1:bf46edcd6b4f | 3 | const char* Game::LOSE_1 = "Game over"; |
maxint | 1:bf46edcd6b4f | 4 | const char* Game::LOSE_2 = "Press ship to restart"; |
maxint | 0:7e989d0083ff | 5 | const char* Game::SPLASH_1 = "-*- Balls and paddle -*-"; |
maxint | 1:bf46edcd6b4f | 6 | const char* Game::SPLASH_2 = "Press ship to start"; |
maxint | 1:bf46edcd6b4f | 7 | const char* Game::SPLASH_3 = "Made by Maxint."; |
maxint | 0:7e989d0083ff | 8 | |
maxint | 0:7e989d0083ff | 9 | #define WHITE Color565::White |
maxint | 0:7e989d0083ff | 10 | #define BLACK Color565::Black |
maxint | 0:7e989d0083ff | 11 | #define BLUE Color565::Blue |
maxint | 0:7e989d0083ff | 12 | #define RED Color565::Red |
maxint | 0:7e989d0083ff | 13 | #define YELLOW Color565::Yellow |
maxint | 0:7e989d0083ff | 14 | |
maxint | 0:7e989d0083ff | 15 | #define CHAR_WIDTH 8 |
maxint | 0:7e989d0083ff | 16 | #define CHAR_HEIGHT 8 |
maxint | 0:7e989d0083ff | 17 | #define HEIGHT this->disp.getHeight() |
maxint | 0:7e989d0083ff | 18 | #define WIDTH this->disp.getWidth() |
maxint | 0:7e989d0083ff | 19 | |
maxint | 1:bf46edcd6b4f | 20 | // |
maxint | 1:bf46edcd6b4f | 21 | // Initialisation |
maxint | 1:bf46edcd6b4f | 22 | // |
maxint | 0:7e989d0083ff | 23 | |
maxint | 0:7e989d0083ff | 24 | Game::Game() : left(P0_14, PullUp), right(P0_11, PullUp), down(P0_12, PullUp), up(P0_13, PullUp), square(P0_16, PullUp), circle(P0_1, PullUp), led1(P0_9), led2(P0_8), |
maxint | 0:7e989d0083ff | 25 | ain(P0_15), i2c(P0_5, P0_4), disp(P0_19, P0_20, P0_7, P0_21, P0_22, P1_15, P0_2, LCD_ST7735::RGB), accel(this->I2C_ADDR, &disp), vGravity(0, 0.1), ball(&disp), paddle(&disp) |
maxint | 0:7e989d0083ff | 26 | { |
maxint | 0:7e989d0083ff | 27 | this->disp.setOrientation(LCD_ST7735::Rotate270, false); |
maxint | 0:7e989d0083ff | 28 | this->disp.setForegroundColor(WHITE); |
maxint | 0:7e989d0083ff | 29 | this->disp.setBackgroundColor(BLACK); |
maxint | 0:7e989d0083ff | 30 | this->disp.clearScreen(); |
maxint | 0:7e989d0083ff | 31 | |
maxint | 0:7e989d0083ff | 32 | srand(this->ain.read_u16()); |
maxint | 0:7e989d0083ff | 33 | |
maxint | 0:7e989d0083ff | 34 | this->lastUp = false; |
maxint | 0:7e989d0083ff | 35 | this->lastDown = false; |
maxint | 0:7e989d0083ff | 36 | this->mode = true; // mode: true=game, false=graph |
maxint | 0:7e989d0083ff | 37 | |
maxint | 0:7e989d0083ff | 38 | this->nGameTickDelay=25; // game tickdelay can be adjusted using up/down |
maxint | 0:7e989d0083ff | 39 | |
maxint | 0:7e989d0083ff | 40 | for(int i=0; i<NUM_BALLS; i++) |
maxint | 0:7e989d0083ff | 41 | this->aBalls[i]=Ball(&(this->disp)); |
maxint | 0:7e989d0083ff | 42 | |
maxint | 0:7e989d0083ff | 43 | this->snd.reset(); |
maxint | 0:7e989d0083ff | 44 | } |
maxint | 0:7e989d0083ff | 45 | |
maxint | 0:7e989d0083ff | 46 | void Game::initialize() |
maxint | 0:7e989d0083ff | 47 | { |
maxint | 0:7e989d0083ff | 48 | this->disp.clearScreen(); |
maxint | 0:7e989d0083ff | 49 | |
maxint | 0:7e989d0083ff | 50 | this->snd.reset(); |
maxint | 0:7e989d0083ff | 51 | this->nBalls = 4; |
maxint | 0:7e989d0083ff | 52 | this->nScore = 0; |
maxint | 0:7e989d0083ff | 53 | this->nTopWall = 8; |
maxint | 0:7e989d0083ff | 54 | this->fDrawTopWall=true; |
maxint | 0:7e989d0083ff | 55 | |
maxint | 0:7e989d0083ff | 56 | this->initializePaddle(); |
maxint | 0:7e989d0083ff | 57 | this->setNoBalls(); // reset all balls |
maxint | 0:7e989d0083ff | 58 | this->newBall(); // start first ball |
maxint | 0:7e989d0083ff | 59 | this->snd.play("T240 L16 O5 D E F"); |
maxint | 0:7e989d0083ff | 60 | } |
maxint | 0:7e989d0083ff | 61 | |
maxint | 0:7e989d0083ff | 62 | |
maxint | 1:bf46edcd6b4f | 63 | // |
maxint | 1:bf46edcd6b4f | 64 | // Generic methods |
maxint | 1:bf46edcd6b4f | 65 | // |
maxint | 1:bf46edcd6b4f | 66 | |
maxint | 1:bf46edcd6b4f | 67 | void Game::printDouble(double value, int x, int y) |
maxint | 1:bf46edcd6b4f | 68 | { |
maxint | 1:bf46edcd6b4f | 69 | char buffer[10]; |
maxint | 1:bf46edcd6b4f | 70 | int len = sprintf(buffer, "%.1f ", value); |
maxint | 1:bf46edcd6b4f | 71 | |
maxint | 1:bf46edcd6b4f | 72 | this->disp.drawString(font_oem, x, y, buffer); |
maxint | 1:bf46edcd6b4f | 73 | } |
maxint | 1:bf46edcd6b4f | 74 | |
maxint | 1:bf46edcd6b4f | 75 | void Game::drawString(const char* str, int y) |
maxint | 1:bf46edcd6b4f | 76 | { |
maxint | 1:bf46edcd6b4f | 77 | uint8_t width; |
maxint | 1:bf46edcd6b4f | 78 | uint8_t height; |
maxint | 1:bf46edcd6b4f | 79 | |
maxint | 1:bf46edcd6b4f | 80 | this->disp.measureString(font_oem, str, width, height); |
maxint | 1:bf46edcd6b4f | 81 | this->disp.drawString(font_oem, WIDTH / 2 - width / 2, y, str); |
maxint | 1:bf46edcd6b4f | 82 | |
maxint | 1:bf46edcd6b4f | 83 | } |
maxint | 1:bf46edcd6b4f | 84 | |
maxint | 1:bf46edcd6b4f | 85 | void Game::printf(int x, int y, const char *szFormat, ...) |
maxint | 3:a68d4d515c20 | 86 | { // formats: %s, %d, %0.2f |
maxint | 1:bf46edcd6b4f | 87 | char szBuffer[256]; |
maxint | 1:bf46edcd6b4f | 88 | va_list args; |
maxint | 1:bf46edcd6b4f | 89 | |
maxint | 1:bf46edcd6b4f | 90 | va_start(args, szFormat); |
maxint | 1:bf46edcd6b4f | 91 | vsprintf(szBuffer, szFormat, args); |
maxint | 1:bf46edcd6b4f | 92 | va_end(args); |
maxint | 1:bf46edcd6b4f | 93 | this->disp.drawString(font_oem, x, y, szBuffer); |
maxint | 1:bf46edcd6b4f | 94 | } |
maxint | 1:bf46edcd6b4f | 95 | |
maxint | 1:bf46edcd6b4f | 96 | int Game::checkTiltLeftRight() |
maxint | 1:bf46edcd6b4f | 97 | { // check current X-tilting for left-right input (left=-1, right=1) |
maxint | 1:bf46edcd6b4f | 98 | double x, y, z; |
maxint | 1:bf46edcd6b4f | 99 | this->accel.getXYZ(x, y, z); |
maxint | 1:bf46edcd6b4f | 100 | if(x<-0.1) return(-1); |
maxint | 1:bf46edcd6b4f | 101 | else if(x>0.1) return(1); |
maxint | 1:bf46edcd6b4f | 102 | else return(0); |
maxint | 1:bf46edcd6b4f | 103 | } |
maxint | 1:bf46edcd6b4f | 104 | |
maxint | 1:bf46edcd6b4f | 105 | |
maxint | 1:bf46edcd6b4f | 106 | // |
maxint | 1:bf46edcd6b4f | 107 | // Paddle |
maxint | 1:bf46edcd6b4f | 108 | // |
maxint | 1:bf46edcd6b4f | 109 | |
maxint | 0:7e989d0083ff | 110 | void Game::initializePaddle() |
maxint | 0:7e989d0083ff | 111 | { |
maxint | 0:7e989d0083ff | 112 | this->paddle.initialize(WIDTH / 2 - Game::PADDLE_WIDTH/2, HEIGHT - Game::PADDLE_HEIGHT, Game::PADDLE_WIDTH, Game::PADDLE_HEIGHT); |
maxint | 0:7e989d0083ff | 113 | this->fDrawPaddle=true; |
maxint | 0:7e989d0083ff | 114 | } |
maxint | 0:7e989d0083ff | 115 | |
maxint | 0:7e989d0083ff | 116 | |
maxint | 0:7e989d0083ff | 117 | void Game::updatePaddle() |
maxint | 0:7e989d0083ff | 118 | { |
maxint | 0:7e989d0083ff | 119 | if (!this->left.read()) // note: read is LOW (0) when button pressed |
maxint | 0:7e989d0083ff | 120 | this->paddle.move(Vector(-1 * Game::PADDLE_SPEED, 0)); |
maxint | 0:7e989d0083ff | 121 | else if (!this->right.read()) |
maxint | 0:7e989d0083ff | 122 | this->paddle.move(Vector(Game::PADDLE_SPEED, 0)); |
maxint | 0:7e989d0083ff | 123 | else |
maxint | 1:bf46edcd6b4f | 124 | { // move the paddle by tilting the board left or right |
maxint | 2:0caa0607f7bf | 125 | int i=this->checkTiltLeftRight(); |
maxint | 0:7e989d0083ff | 126 | if(i>0) |
maxint | 0:7e989d0083ff | 127 | this->paddle.move(Vector(Game::PADDLE_SPEED, 0)); |
maxint | 0:7e989d0083ff | 128 | else if(i<0) |
maxint | 0:7e989d0083ff | 129 | this->paddle.move(Vector(-1 * Game::PADDLE_SPEED, 0)); |
maxint | 0:7e989d0083ff | 130 | else if(this->paddle.hasChanged()) |
maxint | 0:7e989d0083ff | 131 | paddle.move(Vector(0, 0)); // move to same place to restrict redraws |
maxint | 0:7e989d0083ff | 132 | } |
maxint | 0:7e989d0083ff | 133 | } |
maxint | 0:7e989d0083ff | 134 | |
maxint | 0:7e989d0083ff | 135 | void Game::redrawPaddle() |
maxint | 0:7e989d0083ff | 136 | { // redraw the paddle when moved, or when forced by this->fDrawPaddle (set at bounce) |
maxint | 0:7e989d0083ff | 137 | this->paddle.redraw(this->fDrawPaddle); |
maxint | 0:7e989d0083ff | 138 | this->fDrawPaddle=false; |
maxint | 0:7e989d0083ff | 139 | } |
maxint | 0:7e989d0083ff | 140 | |
maxint | 1:bf46edcd6b4f | 141 | void Game::checkPaddle() |
maxint | 1:bf46edcd6b4f | 142 | { |
maxint | 1:bf46edcd6b4f | 143 | Rectangle rScreen=Rectangle(0,0, WIDTH, HEIGHT); // screen boundary |
maxint | 1:bf46edcd6b4f | 144 | |
maxint | 1:bf46edcd6b4f | 145 | this->paddle.checkBoundary(rScreen); |
maxint | 1:bf46edcd6b4f | 146 | } |
maxint | 1:bf46edcd6b4f | 147 | |
maxint | 1:bf46edcd6b4f | 148 | |
maxint | 1:bf46edcd6b4f | 149 | // |
maxint | 1:bf46edcd6b4f | 150 | // Balls |
maxint | 1:bf46edcd6b4f | 151 | // |
maxint | 1:bf46edcd6b4f | 152 | |
maxint | 0:7e989d0083ff | 153 | void Game::setNoBalls() |
maxint | 0:7e989d0083ff | 154 | { // make sure no balls are active |
maxint | 0:7e989d0083ff | 155 | for(int i=0; i<NUM_BALLS; i++) |
maxint | 0:7e989d0083ff | 156 | this->aBalls[i].fActive=false; |
maxint | 0:7e989d0083ff | 157 | } |
maxint | 0:7e989d0083ff | 158 | |
maxint | 0:7e989d0083ff | 159 | void Game::newBall() |
maxint | 0:7e989d0083ff | 160 | { // add a new ball to the game |
maxint | 0:7e989d0083ff | 161 | for(int i=0; i<NUM_BALLS; i++) |
maxint | 0:7e989d0083ff | 162 | { |
maxint | 0:7e989d0083ff | 163 | if(this->aBalls[i].fActive) |
maxint | 0:7e989d0083ff | 164 | continue; |
maxint | 0:7e989d0083ff | 165 | else |
maxint | 0:7e989d0083ff | 166 | { |
maxint | 0:7e989d0083ff | 167 | this->aBalls[i].initialize(WIDTH / 2 - Game::BALL_RADIUS, this->nTopWall + (HEIGHT-this->nTopWall) / 4 - Game::BALL_RADIUS, Game::BALL_RADIUS, Color565::fromRGB(i==0?0xFF:0x33, i==1?0xFF:0x33, i==2?0xFF:0x33)); |
maxint | 0:7e989d0083ff | 168 | //float ftRandX=rand() % 2 ? 1 : -1; |
maxint | 0:7e989d0083ff | 169 | //float ftRandY=rand() % 2 ? 1 : -1; |
maxint | 0:7e989d0083ff | 170 | //this->aBalls[i].setSpeed(ftRandX, ftRandY); |
maxint | 0:7e989d0083ff | 171 | float ftRandX=((rand() % 20) - 10)/5.0; // left/right at random speed |
maxint | 0:7e989d0083ff | 172 | float ftRandY=((rand() % 10) - 10)/5.0; // up at random speed |
maxint | 0:7e989d0083ff | 173 | this->aBalls[i].vSpeed.set(ftRandX, ftRandY); |
maxint | 0:7e989d0083ff | 174 | this->aBalls[i].fActive=true; |
maxint | 0:7e989d0083ff | 175 | break; |
maxint | 0:7e989d0083ff | 176 | } |
maxint | 0:7e989d0083ff | 177 | } |
maxint | 0:7e989d0083ff | 178 | } |
maxint | 0:7e989d0083ff | 179 | |
maxint | 0:7e989d0083ff | 180 | void Game::updateBalls() |
maxint | 0:7e989d0083ff | 181 | { |
maxint | 0:7e989d0083ff | 182 | for(int i=0; i<NUM_BALLS; i++) |
maxint | 0:7e989d0083ff | 183 | { |
maxint | 0:7e989d0083ff | 184 | if(!this->aBalls[i].fActive) |
maxint | 0:7e989d0083ff | 185 | continue; |
maxint | 0:7e989d0083ff | 186 | |
maxint | 0:7e989d0083ff | 187 | this->aBalls[i].update(); // update the ball position |
maxint | 0:7e989d0083ff | 188 | |
maxint | 0:7e989d0083ff | 189 | // add downward gravity |
maxint | 0:7e989d0083ff | 190 | if(this->aBalls[i].vSpeed.getSize()<10.0) |
maxint | 0:7e989d0083ff | 191 | this->aBalls[i].vSpeed.add(this->vGravity); // add some gravity |
maxint | 0:7e989d0083ff | 192 | |
maxint | 0:7e989d0083ff | 193 | } |
maxint | 0:7e989d0083ff | 194 | } |
maxint | 0:7e989d0083ff | 195 | |
maxint | 0:7e989d0083ff | 196 | void Game::redrawBalls() |
maxint | 0:7e989d0083ff | 197 | { |
maxint | 0:7e989d0083ff | 198 | for(int i=0; i<NUM_BALLS; i++) |
maxint | 0:7e989d0083ff | 199 | { |
maxint | 0:7e989d0083ff | 200 | if(!this->aBalls[i].fActive) |
maxint | 0:7e989d0083ff | 201 | continue; |
maxint | 0:7e989d0083ff | 202 | this->aBalls[i].redraw(); // update the ball position |
maxint | 0:7e989d0083ff | 203 | } |
maxint | 0:7e989d0083ff | 204 | } |
maxint | 0:7e989d0083ff | 205 | |
maxint | 0:7e989d0083ff | 206 | int Game::countBalls() |
maxint | 0:7e989d0083ff | 207 | { |
maxint | 0:7e989d0083ff | 208 | int nResult=0; |
maxint | 0:7e989d0083ff | 209 | for(int i=0; i<NUM_BALLS; i++) |
maxint | 0:7e989d0083ff | 210 | { |
maxint | 0:7e989d0083ff | 211 | if(this->aBalls[i].fActive) |
maxint | 0:7e989d0083ff | 212 | nResult++; |
maxint | 0:7e989d0083ff | 213 | } |
maxint | 0:7e989d0083ff | 214 | return(nResult); |
maxint | 0:7e989d0083ff | 215 | } |
maxint | 0:7e989d0083ff | 216 | |
maxint | 1:bf46edcd6b4f | 217 | void Game::checkNumBalls() |
maxint | 1:bf46edcd6b4f | 218 | { |
maxint | 1:bf46edcd6b4f | 219 | if (this->nBalls == 0) |
maxint | 1:bf46edcd6b4f | 220 | { // game over |
maxint | 1:bf46edcd6b4f | 221 | char buf[256]; |
maxint | 1:bf46edcd6b4f | 222 | this->disp.clearScreen(); |
maxint | 0:7e989d0083ff | 223 | |
maxint | 1:bf46edcd6b4f | 224 | this->drawString(Game::LOSE_1, HEIGHT / 2 - CHAR_HEIGHT); |
maxint | 1:bf46edcd6b4f | 225 | this->drawString(Game::LOSE_2, HEIGHT / 2); |
maxint | 1:bf46edcd6b4f | 226 | sprintf(buf,"Your score: %d ", this->nScore); |
maxint | 1:bf46edcd6b4f | 227 | this->drawString(buf, HEIGHT / 2 + CHAR_HEIGHT / 2 + CHAR_HEIGHT ); |
maxint | 0:7e989d0083ff | 228 | |
maxint | 1:bf46edcd6b4f | 229 | this->snd.play("T120 O3 L4 R4 F C F2 C"); |
maxint | 1:bf46edcd6b4f | 230 | while (this->circle.read()) |
maxint | 1:bf46edcd6b4f | 231 | wait_ms(1); |
maxint | 0:7e989d0083ff | 232 | wait_ms(250); // el-cheapo deboounce |
maxint | 1:bf46edcd6b4f | 233 | this->initialize(); |
maxint | 0:7e989d0083ff | 234 | } |
maxint | 0:7e989d0083ff | 235 | else |
maxint | 1:bf46edcd6b4f | 236 | { |
maxint | 1:bf46edcd6b4f | 237 | this->printf(0, 0, "Balls: %d ", this->nBalls); |
maxint | 0:7e989d0083ff | 238 | } |
maxint | 0:7e989d0083ff | 239 | } |
maxint | 0:7e989d0083ff | 240 | |
maxint | 0:7e989d0083ff | 241 | |
maxint | 0:7e989d0083ff | 242 | |
maxint | 0:7e989d0083ff | 243 | void Game::checkBallsCollision() |
maxint | 0:7e989d0083ff | 244 | { |
maxint | 0:7e989d0083ff | 245 | Rectangle rTop=Rectangle(0, -10, WIDTH, this->nTopWall); // top wall |
maxint | 0:7e989d0083ff | 246 | Rectangle rBottom=Rectangle(0, HEIGHT, WIDTH, HEIGHT+10); // bottom gap |
maxint | 0:7e989d0083ff | 247 | Rectangle rLeft=Rectangle(-10, 0, 0, HEIGHT); // left wall |
maxint | 0:7e989d0083ff | 248 | Rectangle rRight=Rectangle(WIDTH, 0, WIDTH+10, HEIGHT); // right wall |
maxint | 0:7e989d0083ff | 249 | Rectangle rPaddle=Rectangle(paddle.pos.getX(), paddle.pos.getY(), paddle.pos.getX() + Game::PADDLE_WIDTH, HEIGHT+10); // paddle |
maxint | 0:7e989d0083ff | 250 | Rectangle rPaddleLeft=Rectangle(paddle.pos.getX(), paddle.pos.getY(), paddle.pos.getX() + Game::PADDLE_WIDTH/3, HEIGHT+10); // paddle left part |
maxint | 3:a68d4d515c20 | 251 | Rectangle rPaddleMiddle=Rectangle(paddle.pos.getX() + Game::PADDLE_WIDTH/3, paddle.pos.getY(), paddle.pos.getX() + Game::PADDLE_WIDTH/3 + Game::PADDLE_WIDTH/3, HEIGHT+10); // paddle middle part |
maxint | 0:7e989d0083ff | 252 | Rectangle rPaddleRight=Rectangle(paddle.pos.getX()+ Game::PADDLE_WIDTH/3 + Game::PADDLE_WIDTH/3, paddle.pos.getY(), paddle.pos.getX() + Game::PADDLE_WIDTH, HEIGHT+10); // paddle right part |
maxint | 0:7e989d0083ff | 253 | |
maxint | 4:8643d2d93a5f | 254 | Line lPaddleLeft=Line(paddle.pos.getX(), paddle.pos.getY(), paddle.pos.getX() + Game::PADDLE_WIDTH/3, HEIGHT+10); // paddle left part |
maxint | 4:8643d2d93a5f | 255 | Line lPaddleRight=Line(paddle.pos.getX()+ Game::PADDLE_WIDTH/3 + Game::PADDLE_WIDTH/3, paddle.pos.getY(), paddle.pos.getX() + Game::PADDLE_WIDTH, HEIGHT+10); // paddle right part |
maxint | 4:8643d2d93a5f | 256 | |
maxint | 3:a68d4d515c20 | 257 | //printf(0, 20, "Paddle: %d-%d %d-%d ", rPaddleLeft.getX1(), rPaddleLeft.getX2(), rPaddleRight.getX1(), rPaddleRight.getX2()); |
maxint | 3:a68d4d515c20 | 258 | |
maxint | 0:7e989d0083ff | 259 | Ball* pBall; |
maxint | 0:7e989d0083ff | 260 | for(int i=0; i<NUM_BALLS; i++) |
maxint | 0:7e989d0083ff | 261 | { |
maxint | 0:7e989d0083ff | 262 | if(!this->aBalls[i].fActive) |
maxint | 0:7e989d0083ff | 263 | continue; |
maxint | 0:7e989d0083ff | 264 | |
maxint | 0:7e989d0083ff | 265 | pBall=&(this->aBalls[i]); |
maxint | 0:7e989d0083ff | 266 | |
maxint | 0:7e989d0083ff | 267 | if(pBall->collides(rTop) && pBall->vSpeed.isUp()) // top wall |
maxint | 0:7e989d0083ff | 268 | { |
maxint | 0:7e989d0083ff | 269 | pBall->Bounce(Vector(1,-1)); // bounce vertical |
maxint | 0:7e989d0083ff | 270 | this->snd.beepShort(); |
maxint | 0:7e989d0083ff | 271 | this->fDrawTopWall=true; |
maxint | 0:7e989d0083ff | 272 | } |
maxint | 0:7e989d0083ff | 273 | if(pBall->collides(rRight) && pBall->vSpeed.isRight()) // right wall |
maxint | 0:7e989d0083ff | 274 | { |
maxint | 0:7e989d0083ff | 275 | pBall->Bounce(Vector(-1,1)); // bounce horizontal |
maxint | 0:7e989d0083ff | 276 | this->snd.beepShort(); |
maxint | 0:7e989d0083ff | 277 | } |
maxint | 0:7e989d0083ff | 278 | if(pBall->collides(rLeft) && pBall->vSpeed.isLeft()) // left wall |
maxint | 0:7e989d0083ff | 279 | { |
maxint | 0:7e989d0083ff | 280 | pBall->Bounce(Vector(-1,1)); // bounce horizontal |
maxint | 0:7e989d0083ff | 281 | this->snd.beepShort(); |
maxint | 0:7e989d0083ff | 282 | } |
maxint | 0:7e989d0083ff | 283 | if(pBall->collides(rPaddle) && pBall->vSpeed.isDown()) // paddle |
maxint | 0:7e989d0083ff | 284 | { |
maxint | 4:8643d2d93a5f | 285 | if(pBall->collides(lPaddleLeft) || pBall->collides(lPaddleRight) || pBall->collides(rPaddleMiddle)) |
maxint | 3:a68d4d515c20 | 286 | { |
maxint | 4:8643d2d93a5f | 287 | if(pBall->collides(lPaddleLeft)) pBall->vSpeed.add(Vector(-1.3,0)); // left side of paddle has bias to the left |
maxint | 4:8643d2d93a5f | 288 | if(pBall->collides(lPaddleRight)) pBall->vSpeed.add(Vector(1.3,0)); // right side of paddle has bias to the right |
maxint | 4:8643d2d93a5f | 289 | pBall->Bounce(Vector(1,-1)); |
maxint | 4:8643d2d93a5f | 290 | /* |
maxint | 4:8643d2d93a5f | 291 | if(pBall->collides(rPaddleMiddle)) |
maxint | 4:8643d2d93a5f | 292 | { |
maxint | 4:8643d2d93a5f | 293 | pBall->Bounce(Vector(1,-1)); |
maxint | 4:8643d2d93a5f | 294 | // printf(10, 10, "Mid: %0.2f, %0.2f ", pBall->vSpeed.x, pBall->vSpeed.y); |
maxint | 4:8643d2d93a5f | 295 | } |
maxint | 4:8643d2d93a5f | 296 | else if(pBall->collides(rPaddleLeft)) |
maxint | 4:8643d2d93a5f | 297 | { |
maxint | 4:8643d2d93a5f | 298 | Vector vDirection(-1,-2); |
maxint | 4:8643d2d93a5f | 299 | pBall->Bounce(vDirection.getNormalized()); |
maxint | 4:8643d2d93a5f | 300 | //pBall->BounceAgainst(Vector(12,-4)); // left side of paddle has bias to the left |
maxint | 4:8643d2d93a5f | 301 | // printf(10, 10, "Left: %0.2f, %0.2f ", pBall->vSpeed.x, pBall->vSpeed.y); |
maxint | 4:8643d2d93a5f | 302 | } |
maxint | 4:8643d2d93a5f | 303 | else if(pBall->collides(rPaddleRight)) |
maxint | 4:8643d2d93a5f | 304 | { |
maxint | 4:8643d2d93a5f | 305 | Vector vDirection(1,-2); |
maxint | 4:8643d2d93a5f | 306 | pBall->Bounce(vDirection.getNormalized()); |
maxint | 4:8643d2d93a5f | 307 | //pBall->BounceAgainst(Vector(12,4)); // right side of paddle has bias to the right |
maxint | 4:8643d2d93a5f | 308 | // printf(10, 10, "Right: %0.2f, %0.2f ", pBall->vSpeed.x, pBall->vSpeed.y); |
maxint | 4:8643d2d93a5f | 309 | } |
maxint | 4:8643d2d93a5f | 310 | */ |
maxint | 4:8643d2d93a5f | 311 | |
maxint | 4:8643d2d93a5f | 312 | { |
maxint | 4:8643d2d93a5f | 313 | // increase the speed of the ball when hitting the paddle to increase difficulty |
maxint | 4:8643d2d93a5f | 314 | float ftSpeedMax=3.0; |
maxint | 4:8643d2d93a5f | 315 | if(this->nScore>50) |
maxint | 4:8643d2d93a5f | 316 | ftSpeedMax=5.0; |
maxint | 4:8643d2d93a5f | 317 | if(this->nScore>100) |
maxint | 4:8643d2d93a5f | 318 | ftSpeedMax=10.0; |
maxint | 4:8643d2d93a5f | 319 | if(this->nScore>150) |
maxint | 4:8643d2d93a5f | 320 | ftSpeedMax=999.0; |
maxint | 4:8643d2d93a5f | 321 | if(pBall->vSpeed.getSize()<ftSpeedMax) |
maxint | 4:8643d2d93a5f | 322 | pBall->vSpeed.multiply(Vector(1,1.02)); // bounce up from paddle at higher speed |
maxint | 4:8643d2d93a5f | 323 | } |
maxint | 4:8643d2d93a5f | 324 | //printf(10, 10, "Bounce: %0.2f, %0.2f ", pBall->vSpeed.x, pBall->vSpeed.y); |
maxint | 0:7e989d0083ff | 325 | |
maxint | 4:8643d2d93a5f | 326 | |
maxint | 4:8643d2d93a5f | 327 | // force drawing the paddle after redrawing the bounced ball |
maxint | 4:8643d2d93a5f | 328 | this->fDrawPaddle=true; |
maxint | 4:8643d2d93a5f | 329 | |
maxint | 4:8643d2d93a5f | 330 | // make sound and update the score |
maxint | 4:8643d2d93a5f | 331 | this->snd.beepLong(); |
maxint | 4:8643d2d93a5f | 332 | this->nScore++; |
maxint | 4:8643d2d93a5f | 333 | this->printf(100, 0, "Score: %d ", this->nScore); |
maxint | 4:8643d2d93a5f | 334 | |
maxint | 4:8643d2d93a5f | 335 | // add a new ball every 10 points |
maxint | 4:8643d2d93a5f | 336 | if(this->nScore>0 && this->nScore%10==0) |
maxint | 4:8643d2d93a5f | 337 | { |
maxint | 4:8643d2d93a5f | 338 | this->newBall(); |
maxint | 4:8643d2d93a5f | 339 | this->nBalls++; |
maxint | 4:8643d2d93a5f | 340 | this->snd.play("T240 L16 O5 D E F"); |
maxint | 4:8643d2d93a5f | 341 | } |
maxint | 4:8643d2d93a5f | 342 | |
maxint | 4:8643d2d93a5f | 343 | // lower the ceiling every 25 points |
maxint | 4:8643d2d93a5f | 344 | if(this->nScore>0 && this->nScore%25==0) |
maxint | 4:8643d2d93a5f | 345 | { |
maxint | 4:8643d2d93a5f | 346 | this->nTopWall+=3; |
maxint | 4:8643d2d93a5f | 347 | this->fDrawTopWall=true; |
maxint | 4:8643d2d93a5f | 348 | this->snd.play("T240 L16 O5 CDEFG"); |
maxint | 4:8643d2d93a5f | 349 | } |
maxint | 0:7e989d0083ff | 350 | } |
maxint | 0:7e989d0083ff | 351 | } |
maxint | 0:7e989d0083ff | 352 | if(pBall->collides(rBottom) && pBall->vSpeed.isDown()) // bottom gap |
maxint | 0:7e989d0083ff | 353 | { |
maxint | 0:7e989d0083ff | 354 | pBall->clearPrev(); // clear the ball from its previous position |
maxint | 0:7e989d0083ff | 355 | pBall->clear(); // clear the ball from its current position |
maxint | 0:7e989d0083ff | 356 | pBall->vSpeed.set(0,0); |
maxint | 0:7e989d0083ff | 357 | pBall->fActive=false; |
maxint | 0:7e989d0083ff | 358 | this->nBalls--; |
maxint | 0:7e989d0083ff | 359 | if(countBalls()==0) |
maxint | 0:7e989d0083ff | 360 | { |
maxint | 0:7e989d0083ff | 361 | this->newBall(); // start a new ball |
maxint | 0:7e989d0083ff | 362 | this->snd.beepLow(); |
maxint | 0:7e989d0083ff | 363 | } |
maxint | 4:8643d2d93a5f | 364 | this->fDrawPaddle=true; |
maxint | 0:7e989d0083ff | 365 | } |
maxint | 0:7e989d0083ff | 366 | } |
maxint | 0:7e989d0083ff | 367 | } |
maxint | 0:7e989d0083ff | 368 | |
maxint | 1:bf46edcd6b4f | 369 | |
maxint | 1:bf46edcd6b4f | 370 | // |
maxint | 1:bf46edcd6b4f | 371 | // Other gamestuff |
maxint | 1:bf46edcd6b4f | 372 | // |
maxint | 1:bf46edcd6b4f | 373 | |
maxint | 1:bf46edcd6b4f | 374 | void Game::tick() |
maxint | 1:bf46edcd6b4f | 375 | { |
maxint | 1:bf46edcd6b4f | 376 | this->checkButtons(); |
maxint | 1:bf46edcd6b4f | 377 | |
maxint | 1:bf46edcd6b4f | 378 | if (this->mode) { |
maxint | 1:bf46edcd6b4f | 379 | |
maxint | 1:bf46edcd6b4f | 380 | this->updateBalls(); // update the ball positions |
maxint | 1:bf46edcd6b4f | 381 | this->updatePaddle(); |
maxint | 1:bf46edcd6b4f | 382 | |
maxint | 1:bf46edcd6b4f | 383 | this->checkPaddle(); |
maxint | 1:bf46edcd6b4f | 384 | this->checkBallsCollision(); |
maxint | 1:bf46edcd6b4f | 385 | |
maxint | 1:bf46edcd6b4f | 386 | this->redrawBalls(); |
maxint | 1:bf46edcd6b4f | 387 | this->redrawPaddle(); |
maxint | 1:bf46edcd6b4f | 388 | this->redrawTopWall(); |
maxint | 1:bf46edcd6b4f | 389 | |
maxint | 1:bf46edcd6b4f | 390 | //this->checkScore(); |
maxint | 1:bf46edcd6b4f | 391 | this->checkNumBalls(); |
maxint | 1:bf46edcd6b4f | 392 | |
maxint | 1:bf46edcd6b4f | 393 | wait_ms(this->nGameTickDelay); // can be adjusted using up/down |
maxint | 1:bf46edcd6b4f | 394 | } |
maxint | 1:bf46edcd6b4f | 395 | else { |
maxint | 1:bf46edcd6b4f | 396 | this->accel.updateGraph(); |
maxint | 1:bf46edcd6b4f | 397 | wait_ms(100); |
maxint | 1:bf46edcd6b4f | 398 | } |
maxint | 1:bf46edcd6b4f | 399 | } |
maxint | 1:bf46edcd6b4f | 400 | |
maxint | 1:bf46edcd6b4f | 401 | |
maxint | 1:bf46edcd6b4f | 402 | void Game::checkButtons() |
maxint | 1:bf46edcd6b4f | 403 | { |
maxint | 1:bf46edcd6b4f | 404 | if(!this->square.read()) // note: button.read() is false (LOW/0) when pressed |
maxint | 1:bf46edcd6b4f | 405 | { |
maxint | 1:bf46edcd6b4f | 406 | wait_ms(250); // el-cheapo deboounce |
maxint | 1:bf46edcd6b4f | 407 | this->mode = !this->mode; |
maxint | 1:bf46edcd6b4f | 408 | |
maxint | 1:bf46edcd6b4f | 409 | this->disp.clearScreen(); |
maxint | 1:bf46edcd6b4f | 410 | |
maxint | 1:bf46edcd6b4f | 411 | if (!this->mode) |
maxint | 1:bf46edcd6b4f | 412 | { |
maxint | 1:bf46edcd6b4f | 413 | this->accel.resetGraph(); |
maxint | 1:bf46edcd6b4f | 414 | } |
maxint | 1:bf46edcd6b4f | 415 | |
maxint | 1:bf46edcd6b4f | 416 | this->led1.write(this->mode); |
maxint | 1:bf46edcd6b4f | 417 | this->led2.write(!this->mode); |
maxint | 1:bf46edcd6b4f | 418 | } |
maxint | 1:bf46edcd6b4f | 419 | else if(!this->circle.read() && this->mode) // note: button.read() is false (LOW/0) when pressed |
maxint | 1:bf46edcd6b4f | 420 | { |
maxint | 1:bf46edcd6b4f | 421 | bool fMute=this->snd.getMute(); |
maxint | 1:bf46edcd6b4f | 422 | fMute=!fMute; |
maxint | 1:bf46edcd6b4f | 423 | this->snd.setMute(fMute); |
maxint | 1:bf46edcd6b4f | 424 | this->led2.write(fMute); |
maxint | 1:bf46edcd6b4f | 425 | wait_ms(250); // el-cheapo deboounce |
maxint | 1:bf46edcd6b4f | 426 | } |
maxint | 1:bf46edcd6b4f | 427 | else |
maxint | 1:bf46edcd6b4f | 428 | { |
maxint | 1:bf46edcd6b4f | 429 | bool isUp = !this->up.read(); |
maxint | 1:bf46edcd6b4f | 430 | bool isDown = !this->down.read(); |
maxint | 1:bf46edcd6b4f | 431 | |
maxint | 1:bf46edcd6b4f | 432 | if (isUp && isDown) goto end; |
maxint | 1:bf46edcd6b4f | 433 | if (!isUp && !isDown) goto end; |
maxint | 1:bf46edcd6b4f | 434 | |
maxint | 1:bf46edcd6b4f | 435 | if (isUp && this->lastUp) goto end; |
maxint | 1:bf46edcd6b4f | 436 | if (isDown && this->lastDown) goto end; |
maxint | 1:bf46edcd6b4f | 437 | |
maxint | 1:bf46edcd6b4f | 438 | if (isUp) |
maxint | 1:bf46edcd6b4f | 439 | { |
maxint | 1:bf46edcd6b4f | 440 | if(this->nGameTickDelay<1000) this->nGameTickDelay=(float)this->nGameTickDelay*1.20; |
maxint | 1:bf46edcd6b4f | 441 | this->printf(100, 0, "Speed: %d ", this->nGameTickDelay); |
maxint | 1:bf46edcd6b4f | 442 | } |
maxint | 1:bf46edcd6b4f | 443 | else if (isDown) |
maxint | 1:bf46edcd6b4f | 444 | { |
maxint | 1:bf46edcd6b4f | 445 | if(this->nGameTickDelay>5) this->nGameTickDelay=(float)this->nGameTickDelay/1.20; |
maxint | 1:bf46edcd6b4f | 446 | this->printf(100, 0, "Speed: %d ", this->nGameTickDelay); |
maxint | 1:bf46edcd6b4f | 447 | } |
maxint | 1:bf46edcd6b4f | 448 | |
maxint | 1:bf46edcd6b4f | 449 | end: |
maxint | 1:bf46edcd6b4f | 450 | this->lastUp = isUp; |
maxint | 1:bf46edcd6b4f | 451 | this->lastDown = isDown; |
maxint | 1:bf46edcd6b4f | 452 | } |
maxint | 1:bf46edcd6b4f | 453 | } |
maxint | 1:bf46edcd6b4f | 454 | |
maxint | 1:bf46edcd6b4f | 455 | |
maxint | 1:bf46edcd6b4f | 456 | void Game::showSplashScreen() |
maxint | 1:bf46edcd6b4f | 457 | { |
maxint | 1:bf46edcd6b4f | 458 | this->drawString(Game::SPLASH_1, HEIGHT / 2 - CHAR_HEIGHT / 2); |
maxint | 1:bf46edcd6b4f | 459 | this->drawString(Game::SPLASH_2, HEIGHT / 2 + CHAR_HEIGHT / 2); |
maxint | 1:bf46edcd6b4f | 460 | this->drawString(Game::SPLASH_3, HEIGHT / 2 + CHAR_HEIGHT / 2 + 2*CHAR_HEIGHT); |
maxint | 1:bf46edcd6b4f | 461 | |
maxint | 1:bf46edcd6b4f | 462 | while (this->circle.read()) |
maxint | 1:bf46edcd6b4f | 463 | wait_ms(1); |
maxint | 1:bf46edcd6b4f | 464 | wait_ms(250); // el-cheapo deboounce |
maxint | 1:bf46edcd6b4f | 465 | |
maxint | 1:bf46edcd6b4f | 466 | this->initialize(); // start a new game |
maxint | 1:bf46edcd6b4f | 467 | } |
maxint | 1:bf46edcd6b4f | 468 | |
maxint | 1:bf46edcd6b4f | 469 | |
maxint | 0:7e989d0083ff | 470 | void Game::redrawTopWall() |
maxint | 0:7e989d0083ff | 471 | { |
maxint | 0:7e989d0083ff | 472 | if(this->fDrawTopWall) |
maxint | 0:7e989d0083ff | 473 | { |
maxint | 0:7e989d0083ff | 474 | int nTop=max(this->nTopWall-2, 8); |
maxint | 0:7e989d0083ff | 475 | this->disp.fillRect(0, 8, WIDTH, nTop, Color565::Black); |
maxint | 0:7e989d0083ff | 476 | this->disp.fillRect(0, nTop, WIDTH, this->nTopWall, Color565::Purple); |
maxint | 0:7e989d0083ff | 477 | this->fDrawTopWall=false; |
maxint | 0:7e989d0083ff | 478 | } |
maxint | 0:7e989d0083ff | 479 | } |