Mini Pool game for two players

Dependencies:   PokittoLib

/media/uploads/Pokitto/minipool.bin.1.gif

This is a small, fully documented Mini Pool game for 2 players intended as a programming tutorial

Files at this revision

API Documentation at this revision

Comitter:
Pokitto
Date:
Fri Oct 12 17:09:04 2018 +0000
Commit message:
initial

Changed in this revision

GameObject.cpp Show annotated file Show diff for this revision Revisions of this file
GameObject.h Show annotated file Show diff for this revision Revisions of this file
My_settings.h Show annotated file Show diff for this revision Revisions of this file
PokittoLib.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 1850dcae680f GameObject.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GameObject.cpp	Fri Oct 12 17:09:04 2018 +0000
@@ -0,0 +1,135 @@
+#include "Pokitto.h"
+#include "GameObject.h"
+
+Pokitto::Display gobj_d;
+int flipper = 0;
+
+
+GameObject::GameObject() {
+    x = random(-20,20);
+    y = random(-15,15);
+    vx = 0;//random(-100,100)/100.0f;
+    vy = 0;//random(-100,100)/100.0f;
+    width = 5;
+    height = 5; 
+    if (flipper) color = 7; //random(3,12);
+    else color = 4;
+    flipper = 1 - flipper;
+    if (color == 5) color = 2;
+    orig_color = color;
+    bounce = 0.9f;
+    acceleration = 0.15f;
+    friction = 0.98f; 
+    mass = 1.0f;   
+    centerX = 55+x;
+    centerY = 44+y;
+    movable=true; 
+    visible=true;
+    active=true;
+}
+
+void GameObject::draw() {
+    if (!visible) return;
+    gobj_d.setColor(color);
+    gobj_d.fillRect(55-width/2+x,44-height/2+y,width,height);  
+    centerX = 55+x;
+    centerY = 44+y;  
+}
+
+void GameObject::move() {
+    if (!active) return;
+    if (!movable) return;
+    x += vx;
+    y += vy;
+    centerX = 55+x;
+    centerY = 44+y;   
+    vx *= friction;
+    vy *= friction;
+    if (abs(vx*10) < 1 && abs(vy*10) <1) vx = vy = 0; // practically stopped
+}
+ 
+bool GameObject::isMoving() {
+    if (vx == 0 && vy == 0) return false;
+    return true;    
+} 
+
+void GameObject::remove() {
+    active=false;
+    visible=false;
+    vx = 0;
+    vy = 0;
+}
+ 
+void GameObject::moveX(float n) {
+    x += n;  
+    centerX = 55+x;  
+}
+     
+void GameObject::moveY(float n) {
+    y += n;    
+    centerY = 44+y;
+}    
+
+void GameObject::accelerateX(float a) {
+    vx += a*acceleration; 
+}
+
+void GameObject::accelerateY(float a) {
+    vy += a*acceleration; 
+}
+
+void GameObject::bounceY() {
+    vy = -vy * bounce; 
+}
+
+void GameObject::bounceX() {
+    vx = -vx * bounce; 
+}
+
+void GameObject::bounceY(float bouncefactor) {
+    vy = -vy * (bounce*bouncefactor); 
+}
+
+void GameObject::bounceX(float bouncefactor) {
+    vx = -vx * (bounce*bouncefactor); 
+}
+
+bool GameObject::checkCollision(GameObject& other) {
+    if (active == false || other.active == false) return false;
+    other.color = 2;
+    if ((x + width/2 >= other.x - other.width/2) && (x - width/2 < other.x + other.width/2) && (y + height/2 >= other.y - other.height/2) && (y - height/2 < other.y + other.height/2)) return true; 
+    other.color = other.orig_color;
+    return false;
+}
+
+void GameObject::doCollision(GameObject& other) { 
+    if (active == false || other.active == false) return;
+    if (!other.movable) {
+        //movable vs. immovable collision
+        if (x - width/2 < other.x - other.width/2 || x + width/2 > other.x + other.width/2) {
+            // collision in vertical wall
+            bounceX();  
+        } else {
+            // collision in horizontal wall
+            bounceY();  
+    }
+    } else {
+        // movable vs. movable
+        //vx = ((mass-other.mass)/sum_mass)*vx + ((other.mass*2)/sum_mass)*other.vx;
+        //other.vx = ((mass*2)/sum_mass)*vx + ((other.mass-mass)/sum_mass)*other.vx;  
+        //vy = ((mass-other.mass)/sum_mass)*vy + ((other.mass*2)/sum_mass)*other.vy;
+        //other.vy = ((mass*2)/sum_mass)*vy + ((other.mass-mass)/sum_mass)*other.vy; 
+        float tvx, tvy;
+        tvx = vx; tvy = vy;
+        vx = other.vx*bounce; vy = other.vy*bounce;
+        other.vx = tvx*bounce; other.vy = tvy*bounce;
+    }
+    // move the object out of each other to avoid more collisions
+    int i=0;
+    while (checkCollision(other) && ++i < 10) {move(); if (other.movable) other.move();}
+}
+
+void GameObject::setColor(int c) {
+    color = c;
+    orig_color = c;    
+}
\ No newline at end of file
diff -r 000000000000 -r 1850dcae680f GameObject.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GameObject.h	Fri Oct 12 17:09:04 2018 +0000
@@ -0,0 +1,53 @@
+#ifndef GAMEOBJECT_H
+#define GAMEOBJECT_H
+ 
+
+class GameObject {
+    
+public:
+    float x; // paikka pelikentällä
+    float y;
+    float centerX; // paikka ruudulla
+    float centerY; 
+    float vx;
+    float vy;
+    float acceleration;
+    float friction;
+    float width;
+    float height;
+    float bounce;
+    float mass;
+    
+    int color;
+    int orig_color;
+    bool visible;
+    bool movable;
+    bool active;
+    
+    void setColor(int);
+    bool isMoving();
+    
+    virtual void draw();
+    virtual void move();
+    virtual void remove();
+    
+    void accelerateX(float);
+    void accelerateY(float);
+    
+    void moveX(float);
+    void moveY(float);
+    
+    void bounceX(float); 
+    void bounceY(float);
+    
+    void bounceX();
+    void bounceY();
+    
+    virtual bool checkCollision(GameObject&);
+    virtual void doCollision(GameObject&);
+    
+    GameObject();
+    
+};
+
+#endif
\ No newline at end of file
diff -r 000000000000 -r 1850dcae680f My_settings.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/My_settings.h	Fri Oct 12 17:09:04 2018 +0000
@@ -0,0 +1,16 @@
+/* This is an example of how to set your own project settings 
+
+To use
+
+1. make a copy of this file by pressing right mouse button on top of this file 
+in the mbed online compiler, then choose "Clone"
+
+2. See that the copy is in the root of the project (where your main cpp files are), not inside the PokittoLib folder
+Drag & drop to move if needed
+
+3. rename the clone to "My_settings.h"
+
+*/
+
+#define PROJ_HIRES 0
+#define PROJ_ENABLE_SOUND 0
\ No newline at end of file
diff -r 000000000000 -r 1850dcae680f PokittoLib.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PokittoLib.lib	Fri Oct 12 17:09:04 2018 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/Pokitto/code/PokittoLib/#123aefc978a7
diff -r 000000000000 -r 1850dcae680f main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Oct 12 17:09:04 2018 +0000
@@ -0,0 +1,227 @@
+#include "Pokitto.h"    // Include the Pokitto library
+#include "GameObject.h" // Include the simple physics library
+
+Pokitto::Core mygame; // Create a Pokitto app/game object
+
+#define MAX_BALL_OBJECTS 7 // max amount of balls
+
+GameObject balls[MAX_BALL_OBJECTS]; // Array of balls
+GameObject player;   // player-object (for aiming)
+GameObject bumpers[4]; // Four sides of table
+GameObject pockets[4]; // Four pockets
+
+enum GameStates { Start, Aim, Hit, Win }; // Game states
+
+/** global variables (accessible from everywhere in the code **/
+GameStates gamestate = Start; 
+int ballsinplay = 7; // how many balls in the game
+int player1points = 0; 
+int player2points = 0;
+int gameturn = 1; // which players turn
+bool player_pocketed = false; //continue turn if player pocketed their own color ball on their turn
+
+// starting arrangement of the balls
+void initballs(){
+    balls[0].x = -30; balls[0].y =   0; balls[0].active = true; balls[0].visible = true; balls[0].setColor(1); 
+    balls[1].x = 10; balls[1].y =   0; balls[1].active = true; balls[1].visible = true; balls[1].setColor(7);
+    balls[2].x = 10; balls[2].y =  10; balls[2].active = true; balls[2].visible = true; balls[2].setColor(4); 
+    balls[3].x = 10; balls[3].y = -10; balls[3].active = true; balls[3].visible = true; balls[3].setColor(4); 
+    balls[4].x = 20; balls[4].y = -10; balls[4].active = true; balls[4].visible = true; balls[4].setColor(7); 
+    balls[5].x = 20; balls[5].y =   0; balls[5].active = true; balls[5].visible = true; balls[5].setColor(4);
+    balls[6].x = 20; balls[6].y =  10; balls[6].active = true; balls[6].visible = true; balls[6].setColor(7);     
+} 
+
+/** MAIN is where a typical C++ program begins executing the program **/
+
+int main () { 
+    mygame.begin(); // start Pokitto engine (display, buttons, sound)
+    mygame.display.setFont(fontTiny); // Tiny font active
+    mygame.display.bgcolor = 13; // Background color to dark green (13)
+    mygame.display.invisiblecolor = 13; // Make text background (13) invisible
+    
+    /** PROGRAM LOOP... runs the entire life of the program
+    ie. as long as  mygame.isRunning() returns logical true (=1) **/
+    
+    while (mygame.isRunning()) {
+        
+        // Draw table bumpers
+        for (int i=0; i<4; i++) bumpers[i].draw();
+        // Draw table pockets
+        for (int i=0; i<4; i++) pockets[i].draw();
+        // Calculate balls vs. bumpers collisions
+        for (int i=0; i<ballsinplay; i++) {
+            for (int j=0; j<4; j++) {
+             if (balls[i].checkCollision(bumpers[j]) && gamestate != Start) {
+                    balls[i].doCollision(bumpers[j]);        
+                 }
+            }
+        }
+        
+        // Calculate balls vs. balls collisions
+        for (int i=0; i<ballsinplay; i++) {
+            for (int j=0; j<ballsinplay; j++) {
+             if (i != j) { // do not check collision with self!!
+                    if (balls[i].checkCollision(balls[j]) && gamestate != Start) {
+                        balls[i].doCollision(balls[j]);        
+                    }
+                }
+            }
+        }
+        
+        // Calculate balls colliding with pockets
+        for (int i=0; i<ballsinplay; i++) {
+            for (int j=0; j<4; j++) {
+                if (balls[i].checkCollision(pockets[j]) && gamestate != Start) {
+                    // Ball in pocket!
+                    balls[i].remove(); // remove ball from table
+                    if (balls[i].color == 7) {
+                        player1points++; // red ball point for player 1
+                        if (gameturn == 1) player_pocketed = true; // player gets a new shot!
+                        }
+                    if (balls[i].color == 4) {
+                        player2points++; // yellow ball point for player 2
+                        if (gameturn == 2) player_pocketed = true; // player gets a new shot!
+                    }
+                    if (balls[i].color == 1) {
+                        // White ball in pocket, player lost
+                        if (gameturn==1) { player1points=0; player2points = (ballsinplay-1)/2; } //player 2 vwins
+                        else { player1points = (ballsinplay-1)/2; player2points = 0;} //player 1 wins
+                    }
+                    if (player1points == (ballsinplay-1)/2 || player2points == (ballsinplay-1)/2) gamestate = Win;
+                }
+            }
+        }
+        
+        /** UPDATE loop, refresh the display **/
+        if (mygame.update()) {  
+            int balls_stopped = 0; // for calculating balls that have come to a standstill
+            // Draw balls 
+            for (int i=0; i<ballsinplay; i++) balls[i].draw();
+            // Select white color for text
+            mygame.display.color = 1; 
+            switch (gamestate) {
+            case Start:
+                /** case Start ... initialize table and game **/
+                mygame.display.print(16,8,"MiniPool");
+                mygame.display.print(16,16,"Press A");
+                
+                // Zero points and variables
+                player1points = 0; 
+                player2points = 0;
+                gameturn = 1;
+                
+                // Put balls in starting position
+                initballs();
+                
+                /** Table **/
+                // Top bumper in place
+                bumpers[0].x = 0; bumpers[0].y = -49; bumpers[0].width = 110; bumpers[0].height=20; 
+                bumpers[0].setColor(5); 
+                // Bottom bumper in place
+                bumpers[1].x = 0; bumpers[1].y = 49; bumpers[1].width = 110; bumpers[1].height=20; 
+                bumpers[1].setColor(5); 
+                // Left bumper in place
+                bumpers[2].x = -59; bumpers[2].y = 0; bumpers[2].width = 20; bumpers[2].height=88; 
+                bumpers[2].setColor(5); 
+                // Right bumper in place
+                bumpers[3].x = 59; bumpers[3].y = 0; bumpers[3].width = 20; bumpers[3].height=88; 
+                bumpers[3].setColor(5); 
+                // Make bumpers stationary
+                bumpers[0].movable = bumpers[1].movable = bumpers[2].movable = bumpers[3].movable = false;        
+                
+                /** Pockets **/
+                // Left top pocket in place
+                pockets[0].x = -59; pockets[0].y = -49; pockets[0].width = 30; pockets[0].height=30; 
+                pockets[0].setColor(0); 
+                // Right top pocket in place
+                pockets[1].x = 59; pockets[1].y = -49; pockets[1].width = 30; pockets[1].height=30; 
+                pockets[1].setColor(0); 
+                // Right bottom pocket in place
+                pockets[2].x = 59; pockets[2].y = 49; pockets[2].width = 30; pockets[2].height=30; 
+                pockets[2].setColor(0); 
+                // Left bottom pocket in place
+                pockets[3].x = -59; pockets[3].y = 49; pockets[3].width = 30; pockets[3].height=30; 
+                pockets[3].setColor(0); 
+                // Make all pockets stationary
+                bumpers[0].movable = pockets[1].movable = pockets[2].movable = pockets[3].movable = false; 
+                
+                // if A pressed, move to Aim game state
+                if (mygame.buttons.released(BTN_A)) {
+                    player.x = balls[0].x; player.y = balls[0].y;
+                    gamestate = Aim;
+                    }
+                break;
+            case Aim:
+                player_pocketed = false; // put this back to false, its for checking if a ball was pocketed during the Hit state
+                /** case Aim ... in this gamestate, player moves targetting line **/
+                if (gameturn == 1) {
+                   mygame.display.color = 7; // red
+                   mygame.display.print(16,8,"player 1 turn");   
+                } else {
+                   mygame.display.color = 4; // yellow
+                   mygame.display.print(16,8,"player 2 turn");  
+                }
+    
+                // draw targetting line
+                mygame.display.setColor(1); // white
+                mygame.display.drawLine(player.centerX,player.centerY,balls[0].centerX,balls[0].centerY);
+                // check button presses
+                if (mygame.buttons.rightBtn()) player.moveX(1);
+                if (mygame.buttons.leftBtn()) player.moveX(-1);
+                if (mygame.buttons.upBtn()) player.moveY(-1);
+                if (mygame.buttons.downBtn()) player.moveY(1);
+                // if button A released, a hit begins
+                if (mygame.buttons.released(BTN_A)) {
+                    // give initial speed to the cue ball (the white ball)
+                    balls[0].vx = (player.x - balls[0].x) * balls[0].acceleration; // X direction strength
+                    balls[0].vy = (player.y - balls[0].y) * balls[0].acceleration; // Y direction strength
+                    gamestate = Hit; // go to next gamestate
+                }
+                break;
+            case Hit:
+                /** case Hit ... in this state balls move until they are all stopped **/
+                // now count all stationary balls
+                for (int i=0; i < ballsinplay; i++) {
+                    // if ball is stationary or no longer on the table
+                    if (balls[i].isMoving() == false || balls[i].active == false) { 
+                         balls_stopped++; // count inactive balls
+                    }
+                }
+                if (balls_stopped == ballsinplay) {
+                    // all balls in play are no longer active, time for a new shot
+                    player.x = balls[0].x; player.y = balls[0].y;
+                    if (player_pocketed == false) {
+                        // player did not pocket any of his/her own balls, so does not get a new turn
+                        if (gameturn == 1) gameturn = 2;
+                        else gameturn = 1; // change game turn
+                    }
+                    gamestate = Aim;
+                }
+                break;
+            case Win:
+                /** case Win ... player 1 or 2 won **/
+                if (player1points > player2points) {
+                   mygame.display.color = 7; // red
+                   mygame.display.print(16,8,"player 1 WON!");   
+                } else {
+                   mygame.display.color = 4; // yellow
+                   mygame.display.print(16,8,"player 2 WON!");  
+                }
+                mygame.display.color = 1; // white
+                mygame.display.print(16,16,"Press A");
+                // if button A released, game begins again
+                if (mygame.buttons.released(BTN_A)) {
+                    gamestate = Start;
+                }
+                break;
+                
+            }          
+        } 
+        // Calculate movement of balls (outside of display update loop, we get more calculations and therefore more accuracy like this!)
+        for (int i = 0; i< ballsinplay; i++) balls[i].move();
+        // needed for updating targetting line 
+        player.move();
+        // draw white ball always, even outside display update loop (speed streak effect!)
+        balls[0].draw();
+    }    
+}
\ No newline at end of file