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

Files at this revision

API Documentation at this revision

Comitter:
maxint
Date:
Mon Feb 09 08:34:32 2015 +0000
Parent:
0:7e989d0083ff
Child:
2:0caa0607f7bf
Commit message:
Some more clean-up and commenting

Changed in this revision

Game.cpp Show annotated file Show diff for this revision Revisions of this file
Game.h Show annotated file Show diff for this revision Revisions of this file
RETRO_BallsAndThings.lib Show annotated file Show diff for this revision Revisions of this file
--- 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
--- a/Game.h	Fri Feb 06 10:18:41 2015 +0000
+++ b/Game.h	Mon Feb 09 08:34:32 2015 +0000
@@ -64,37 +64,30 @@
     bool lastDown;
     int nGameTickDelay;     // delay during game-tick
 
-    void printDouble(double value, int x, int y);
-    
     void initialize();
     
+    void printDouble(double value, int x, int y);
     void drawString(const char* str, int y);
+    void printf(int x, int y, const char *szFormat, ...);
+    int checkTiltLeftRight();
     
-
     void initializePaddle();
     void updatePaddle();
     void redrawPaddle();
-/*
-    void clearPaddle();
-    void drawPaddle();
-*/
-    //void initializeBall();
-    //void initializeBalls();
+    void checkPaddle();
+
     void setNoBalls();
     void newBall();
     void updateBalls();
     void redrawBalls();
     int countBalls();
+    void checkNumBalls();
     void checkBallsCollision();
+
     void redrawTopWall();
-    
-    int checkTilt();
     void checkButtons();
 
-    void checkPaddle();
     //void checkCollision();
-    void printf(int x, int y, const char *szFormat, ...);
-    void checkBalls();
     
     public:
         Game();
--- a/RETRO_BallsAndThings.lib	Fri Feb 06 10:18:41 2015 +0000
+++ b/RETRO_BallsAndThings.lib	Mon Feb 09 08:34:32 2015 +0000
@@ -1,1 +1,1 @@
-RETRO_BallsAndThings#71185a0aadfc
+http://developer.mbed.org/users/maxint/code/RETRO_BallsAndThings/#71185a0aadfc