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
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