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
Diff: Game.cpp
- Revision:
- 1:bf46edcd6b4f
- Parent:
- 0:7e989d0083ff
- Child:
- 2:0caa0607f7bf
--- a/Game.cpp Fri Feb 06 10:18:41 2015 +0000 +++ b/Game.cpp Mon Feb 09 08:34:32 2015 +0000 @@ -1,11 +1,10 @@ #include "Game.h" -const char* Game::LOSE_1 = "Game over."; -const char* Game::LOSE_2 = "Press ship to restart."; +const char* Game::LOSE_1 = "Game over"; +const char* Game::LOSE_2 = "Press ship to restart"; const char* Game::SPLASH_1 = "-*- Balls and paddle -*-"; -const char* Game::SPLASH_2 = "Press ship to start."; -const char* Game::SPLASH_3 = "Made by Maxint"; - +const char* Game::SPLASH_2 = "Press ship to start"; +const char* Game::SPLASH_3 = "Made by Maxint."; #define WHITE Color565::White #define BLACK Color565::Black @@ -18,7 +17,9 @@ #define HEIGHT this->disp.getHeight() #define WIDTH this->disp.getWidth() - +// +// Initialisation +// 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), 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) @@ -42,14 +43,6 @@ this->snd.reset(); } -void Game::printDouble(double value, int x, int y) -{ - char buffer[10]; - int len = sprintf(buffer, "%.1f ", value); - - this->disp.drawString(font_oem, x, y, buffer); -} - void Game::initialize() { this->disp.clearScreen(); @@ -67,6 +60,53 @@ } +// +// Generic methods +// + +void Game::printDouble(double value, int x, int y) +{ + char buffer[10]; + int len = sprintf(buffer, "%.1f ", value); + + this->disp.drawString(font_oem, x, y, buffer); +} + +void Game::drawString(const char* str, int y) +{ + uint8_t width; + uint8_t height; + + this->disp.measureString(font_oem, str, width, height); + this->disp.drawString(font_oem, WIDTH / 2 - width / 2, y, str); + +} + +void Game::printf(int x, int y, const char *szFormat, ...) +{ + char szBuffer[256]; + va_list args; + + va_start(args, szFormat); + vsprintf(szBuffer, szFormat, args); + va_end(args); + this->disp.drawString(font_oem, x, y, szBuffer); +} + +int Game::checkTiltLeftRight() +{ // check current X-tilting for left-right input (left=-1, right=1) + double x, y, z; + this->accel.getXYZ(x, y, z); + if(x<-0.1) return(-1); + else if(x>0.1) return(1); + else return(0); +} + + +// +// Paddle +// + void Game::initializePaddle() { this->paddle.initialize(WIDTH / 2 - Game::PADDLE_WIDTH/2, HEIGHT - Game::PADDLE_HEIGHT, Game::PADDLE_WIDTH, Game::PADDLE_HEIGHT); @@ -81,7 +121,7 @@ else if (!this->right.read()) this->paddle.move(Vector(Game::PADDLE_SPEED, 0)); else - { + { // move the paddle by tilting the board left or right int i=this->checkTilt(); if(i>0) this->paddle.move(Vector(Game::PADDLE_SPEED, 0)); @@ -98,6 +138,18 @@ this->fDrawPaddle=false; } +void Game::checkPaddle() +{ + Rectangle rScreen=Rectangle(0,0, WIDTH, HEIGHT); // screen boundary + + this->paddle.checkBoundary(rScreen); +} + + +// +// Balls +// + void Game::setNoBalls() { // make sure no balls are active for(int i=0; i<NUM_BALLS; i++) @@ -162,122 +214,30 @@ return(nResult); } - - - - -void Game::tick() -{ - this->checkButtons(); - - if (this->mode) { - - this->updateBalls(); // update the ball positions - this->updatePaddle(); - - this->checkPaddle(); - this->checkBallsCollision(); - - this->redrawBalls(); - this->redrawPaddle(); - this->redrawTopWall(); - - //this->checkScore(); - this->checkBalls(); - - wait_ms(this->nGameTickDelay); // can be adjusted using up/down - } - else { - this->accel.updateGraph(); - wait_ms(100); - } -} +void Game::checkNumBalls() +{ + if (this->nBalls == 0) + { // game over + char buf[256]; + this->disp.clearScreen(); -int Game::checkTilt() -{ // move the paddle by tilting the board left or righr - double x, y, z; - this->accel.getXYZ(x, y, z); - if(x<-0.1) return(-1); - else if(x>0.1) return(1); - else return(0); -} - -void Game::checkButtons() -{ - if(!this->square.read()) // note: button.read() is false (LOW/0) when pressed - { - wait_ms(250); // el-cheapo deboounce - this->mode = !this->mode; + this->drawString(Game::LOSE_1, HEIGHT / 2 - CHAR_HEIGHT); + this->drawString(Game::LOSE_2, HEIGHT / 2); + sprintf(buf,"Your score: %d ", this->nScore); + this->drawString(buf, HEIGHT / 2 + CHAR_HEIGHT / 2 + CHAR_HEIGHT ); - this->disp.clearScreen(); - - if (!this->mode) - { - this->accel.resetGraph(); - } - - this->led1.write(this->mode); - this->led2.write(!this->mode); - } - else if(!this->circle.read() && this->mode) // note: button.read() is false (LOW/0) when pressed - { - bool fMute=this->snd.getMute(); - fMute=!fMute; - this->snd.setMute(fMute); - this->led2.write(fMute); + this->snd.play("T120 O3 L4 R4 F C F2 C"); + while (this->circle.read()) + wait_ms(1); wait_ms(250); // el-cheapo deboounce + this->initialize(); } else - { - bool isUp = !this->up.read(); - bool isDown = !this->down.read(); - - if (isUp && isDown) goto end; - if (!isUp && !isDown) goto end; - - if (isUp && this->lastUp) goto end; - if (isDown && this->lastDown) goto end; - - if (isUp) - { - if(this->nGameTickDelay<1000) this->nGameTickDelay=(float)this->nGameTickDelay*1.20; - this->printf(100, 0, "Speed: %d ", this->nGameTickDelay); - } - else if (isDown) - { - if(this->nGameTickDelay>5) this->nGameTickDelay=(float)this->nGameTickDelay/1.20; - this->printf(100, 0, "Speed: %d ", this->nGameTickDelay); - } - -end: - this->lastUp = isUp; - this->lastDown = isDown; + { + this->printf(0, 0, "Balls: %d ", this->nBalls); } } -void Game::drawString(const char* str, int y) -{ - uint8_t width; - uint8_t height; - - this->disp.measureString(font_oem, str, width, height); - this->disp.drawString(font_oem, WIDTH / 2 - width / 2, y, str); - -} - -void Game::showSplashScreen() -{ - this->drawString(Game::SPLASH_1, HEIGHT / 2 - CHAR_HEIGHT / 2); - this->drawString(Game::SPLASH_2, HEIGHT / 2 + CHAR_HEIGHT / 2); - this->drawString(Game::SPLASH_3, HEIGHT / 2 + CHAR_HEIGHT / 2 + 2*CHAR_HEIGHT); - - while (this->circle.read()) - wait_ms(1); - wait_ms(250); // el-cheapo deboounce - - this->initialize(); // start a new game -} - void Game::checkBallsCollision() @@ -374,6 +334,107 @@ } } + +// +// Other gamestuff +// + +void Game::tick() +{ + this->checkButtons(); + + if (this->mode) { + + this->updateBalls(); // update the ball positions + this->updatePaddle(); + + this->checkPaddle(); + this->checkBallsCollision(); + + this->redrawBalls(); + this->redrawPaddle(); + this->redrawTopWall(); + + //this->checkScore(); + this->checkNumBalls(); + + wait_ms(this->nGameTickDelay); // can be adjusted using up/down + } + else { + this->accel.updateGraph(); + wait_ms(100); + } +} + + +void Game::checkButtons() +{ + if(!this->square.read()) // note: button.read() is false (LOW/0) when pressed + { + wait_ms(250); // el-cheapo deboounce + this->mode = !this->mode; + + this->disp.clearScreen(); + + if (!this->mode) + { + this->accel.resetGraph(); + } + + this->led1.write(this->mode); + this->led2.write(!this->mode); + } + else if(!this->circle.read() && this->mode) // note: button.read() is false (LOW/0) when pressed + { + bool fMute=this->snd.getMute(); + fMute=!fMute; + this->snd.setMute(fMute); + this->led2.write(fMute); + wait_ms(250); // el-cheapo deboounce + } + else + { + bool isUp = !this->up.read(); + bool isDown = !this->down.read(); + + if (isUp && isDown) goto end; + if (!isUp && !isDown) goto end; + + if (isUp && this->lastUp) goto end; + if (isDown && this->lastDown) goto end; + + if (isUp) + { + if(this->nGameTickDelay<1000) this->nGameTickDelay=(float)this->nGameTickDelay*1.20; + this->printf(100, 0, "Speed: %d ", this->nGameTickDelay); + } + else if (isDown) + { + if(this->nGameTickDelay>5) this->nGameTickDelay=(float)this->nGameTickDelay/1.20; + this->printf(100, 0, "Speed: %d ", this->nGameTickDelay); + } + +end: + this->lastUp = isUp; + this->lastDown = isDown; + } +} + + +void Game::showSplashScreen() +{ + this->drawString(Game::SPLASH_1, HEIGHT / 2 - CHAR_HEIGHT / 2); + this->drawString(Game::SPLASH_2, HEIGHT / 2 + CHAR_HEIGHT / 2); + this->drawString(Game::SPLASH_3, HEIGHT / 2 + CHAR_HEIGHT / 2 + 2*CHAR_HEIGHT); + + while (this->circle.read()) + wait_ms(1); + wait_ms(250); // el-cheapo deboounce + + this->initialize(); // start a new game +} + + void Game::redrawTopWall() { if(this->fDrawTopWall) @@ -384,47 +445,3 @@ this->fDrawTopWall=false; } } - -void Game::checkPaddle() -{ - Rectangle rScreen=Rectangle(0,0, WIDTH, HEIGHT); // screen boundary - - this->paddle.checkBoundary(rScreen); -} - - -void Game::printf(int x, int y, const char *szFormat, ...) -{ - char szBuffer[256]; - va_list args; - - va_start(args, szFormat); - vsprintf(szBuffer, szFormat, args); - va_end(args); - this->disp.drawString(font_oem, x, y, szBuffer); -} - - -void Game::checkBalls() -{ - if (this->nBalls == 0) - { // game over - char buf[256]; - this->disp.clearScreen(); - - this->drawString(Game::LOSE_1, HEIGHT / 2 - CHAR_HEIGHT); - this->drawString(Game::LOSE_2, HEIGHT / 2); - sprintf(buf,"Your score: %d ", this->nScore); - this->drawString(buf, HEIGHT / 2 + CHAR_HEIGHT / 2 + CHAR_HEIGHT ); - - this->snd.play("T120 O3 L4 R4 F C F2 C"); - while (this->circle.read()) - wait_ms(1); - wait_ms(250); // el-cheapo deboounce - this->initialize(); - } - else - { - this->printf(0, 0, "Balls: %d ", this->nBalls); - } -} \ No newline at end of file