A simple game

Dependencies:   4DGL-uLCD-SE mbed-rtos mbed

Fork of rtos_signals by mbed official

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "mbed.h"
00002 #include "rtos.h"
00003 #include <mpr121.h>
00004 #include "uLCD_4DGL.h"
00005 #include "Speaker.h"
00006 
00007 uLCD_4DGL uLCD(p9,p10,p11);
00008 int curr_x_u, curr_y_u, old_x_u, old_y_u; //user's current and old x,y coordinates
00009 int curr_x_e, curr_y_e, old_x_e, old_y_e; //enemy's current and old x,y coordinates
00010 float dx_e, dy_e, fx_e, fy_e; //x,y velocity of enemy and floating point representation of x,y coordinates
00011 int curr_x_b, curr_y_b, old_x_b, old_y_b; //payload's current and old x,y coordinates
00012 int curr_x_p, curr_y_p, old_x_p, old_y_p; //portal's current and old x,y coordinates
00013 Mutex lcd_mutex;
00014 
00015 Thread box; //handles user-payload collision logic
00016 Thread portal; //handles portal logic
00017 
00018 Speaker amp(p21);
00019 
00020 Serial pc(USBTX, USBRX); //debugging
00021 
00022 InterruptIn interrupt(p26);
00023 I2C i2c(p28, p27);
00024 Mpr121 mpr121(&i2c, Mpr121::ADD_VSS);
00025 
00026 int score = 0;
00027 int oldscore = 0;
00028 
00029 volatile int key = -1; //key pressed from keypad; set via interrupts -> volatile so compiler doesn't optimize out value reads
00030 
00031 enum State { START, LIVE, PAUSE, DEAD }; //game status
00032 volatile State s = START;
00033 
00034 //interrupt method that runs when keypad has a new value
00035 void fallInterrupt() {
00036     int key_code=0;
00037     int i=0;
00038     int value=mpr121.read(0x00);
00039     value +=mpr121.read(0x01)<<8;
00040     i=0;
00041     for (i=0; i<12; i++) {
00042         if (((value>>i)&0x01)==1) key_code=i+1;
00043     }
00044     key = key_code;
00045 }
00046 
00047 //handles portal logic
00048 void portal_thread() {
00049     while(s == LIVE || s == PAUSE) {
00050         while(s == PAUSE);
00051         //if payload and portal collide, score a point and randomize portal location
00052         if (((curr_x_p-3 <= curr_x_b+3 && curr_x_p >= curr_x_b) || (curr_x_p+3 >= curr_x_b-3 && curr_x_p <= curr_x_b)) && ((curr_y_p+3 >= curr_y_b-3 && curr_y_p <= curr_y_b) || (curr_y_p-3 <= curr_y_b+3 && curr_y_p >= curr_y_b))) {
00053             score++;
00054             curr_x_p = (rand() % 106) + 10;
00055             curr_y_p = (rand() % 106) + 10;
00056         } else if (((curr_y_p-3 <= curr_y_b+3 && curr_y_p >= curr_y_b) || (curr_y_p+3 >= curr_y_b-3 && curr_y_p <= curr_y_b)) && ((curr_x_p+3 >= curr_x_b-3 && curr_x_p <= curr_x_b) || (curr_x_p-3 <= curr_x_b+3 && curr_x_p >= curr_x_b))) {
00057             score++;
00058             curr_x_p = (rand() % 106) + 10;
00059             curr_y_p = (rand() % 106) + 10;
00060         }
00061         lcd_mutex.lock();
00062         //redraw portal and play note if score has changed
00063         uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
00064         if (score != oldscore) {
00065             amp.PlayNote(1000.0,0.025,0.5);
00066             oldscore = score;
00067             uLCD.rectangle(old_x_p-5, old_y_p-5, old_x_p+5, old_y_p+5, BLACK);
00068             old_x_p = curr_x_p;
00069             old_y_p = curr_y_p;
00070         }
00071         //print score
00072         uLCD.locate(1,15);
00073         uLCD.color(0xFF00FF);
00074         uLCD.printf("%d", score);
00075         lcd_mutex.unlock();
00076     }
00077 }
00078 
00079 //handles user-payload collisions
00080 void box_thread() {
00081     while(s == LIVE) {
00082         old_x_u = curr_x_u;
00083         old_y_u = curr_y_u;
00084         old_x_b = curr_x_b;
00085         old_y_b = curr_y_b;
00086         //if 3 pressed, then go into pause state
00087         if (key==3+1) {
00088             s = PAUSE;
00089             lcd_mutex.lock();
00090             uLCD.locate(1, 2);
00091             uLCD.color(0xFFFF00);
00092             uLCD.printf("PAUSE");
00093             //wait until 3 is unpressed
00094             while(key==3+1);
00095             //wait until 3 is pressed
00096             while(key!=3+1);
00097             //wait until 3 is unpressed
00098             while(key==3+1);
00099             uLCD.locate(1, 2);
00100             uLCD.color(BLACK);
00101             uLCD.printf("PAUSE");
00102             uLCD.color(WHITE);
00103             //countdown from 3
00104             for (int i=3; i>=1; i--) {
00105                 uLCD.locate(1, 2);
00106                 uLCD.printf("%2D", i);
00107                 amp.PlayNote(800.0,0.025,0.5);
00108                 wait(1);
00109             }
00110             uLCD.locate(1,2);
00111             uLCD.color(BLACK);
00112             uLCD.printf("  ");
00113             lcd_mutex.unlock();
00114             //play on
00115             s = LIVE;
00116         }
00117         //really complicated collision logic
00118         //basically checks each way the boxes could be colliding based on which key is pressed (which direction user is moving)
00119         if (key==0+1) {
00120 
00121             if ((curr_x_b+6 > curr_x_u-6 && curr_x_u > curr_x_b) && ((curr_y_u+6 > curr_y_b-6 && curr_y_u <= curr_y_b) || (curr_y_u-6 < curr_y_b+6 && curr_y_u >= curr_y_b))) {
00122                 curr_x_b-=1;
00123                 curr_x_u = curr_x_b+12;
00124                 if (curr_x_b < 7) {
00125                     curr_x_b = 25;
00126                     curr_y_b = 63;
00127                 }
00128             } else {
00129                 curr_x_u-=1;
00130             }
00131             if (curr_x_u < 7) curr_x_u = 7;
00132         } else if (key==8+1) {
00133 
00134             if ((curr_x_b-6 < curr_x_u+6 && curr_x_u < curr_x_b) && ((curr_y_u+6 > curr_y_b-6 && curr_y_u <= curr_y_b) || (curr_y_u-6 < curr_y_b+6 && curr_y_u >= curr_y_b))) {
00135                 curr_x_b+=1;
00136                 curr_x_u = curr_x_b-12;
00137                 if (curr_x_b > 120) {
00138                     curr_x_b = 25;
00139                     curr_y_b = 63;
00140                 }
00141             } else {
00142                 curr_x_u+=1;
00143             }
00144             if (curr_x_u > 120) curr_x_u = 120;
00145         } else if (key==4+1) {
00146 
00147             if ((curr_y_b-6 < curr_y_u+6 && curr_y_u < curr_y_b) && ((curr_x_u+6 > curr_x_b-6 && curr_x_u <= curr_x_b) || (curr_x_u-6 < curr_x_b+6 && curr_x_u >= curr_x_b))) {
00148                 curr_y_b+=1;
00149                 curr_y_u = curr_y_b-12;
00150                 if (curr_y_b > 120) {
00151                     curr_x_b = 25;
00152                     curr_y_b = 63;
00153                 }
00154             } else {
00155                 curr_y_u+=1;
00156             }
00157             if (curr_y_u > 120) curr_y_u = 120;
00158         } else if (key==5+1) {
00159 
00160             if ((curr_y_b+6 > curr_y_u-6 && curr_y_u > curr_y_b) && ((curr_x_u+6 > curr_x_b-6 && curr_x_u <= curr_x_b) || (curr_x_u-6 < curr_x_b+6 && curr_x_u >= curr_x_b))) {
00161                 curr_y_b-=1;
00162                 curr_y_u = curr_y_b+12;
00163                 if (curr_y_b < 7) {
00164                     curr_x_b = 25;
00165                     curr_y_b = 63;
00166                 }
00167             } else {
00168                 curr_y_u-=1;
00169             }
00170             if (curr_y_u < 7) curr_y_u = 7;
00171         }
00172         //redraw user and payload
00173         lcd_mutex.lock();
00174         uLCD.rectangle(20, 58, 30, 68, WHITE);
00175         uLCD.filled_rectangle(old_x_u-5, old_y_u-5, old_x_u+5, old_y_u+5, BLACK);
00176         uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
00177         uLCD.filled_rectangle(old_x_b-5, old_y_b-5, old_x_b+5, old_y_b+5, BLACK);
00178         uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
00179         lcd_mutex.unlock();
00180     }
00181 }
00182 
00183 //main thread handles setup of game, angry ball collision logic, gameover, and game resets
00184 int main() {
00185     //setup keypad interrupts
00186     interrupt.fall(&fallInterrupt);
00187     interrupt.mode(PullUp);
00188     uLCD.baudrate(3000000);
00189     //load in Angry Balls image from SD
00190     uLCD.media_init();
00191     uLCD.set_sector_address(0x003B, 0xD400);
00192     uLCD.display_image(0,0);
00193     //wait 3 seconds to allow keypad time to set up while player can look at the pretty image
00194     wait(3);
00195     uLCD.textbackground_color(WHITE);
00196     uLCD.text_width(1);
00197     uLCD.text_height(1);
00198     uLCD.color(RED);
00199     uLCD.locate(0, 0);
00200     uLCD.printf("Angry Balls!");
00201     uLCD.locate(0, 1);
00202     uLCD.color(BLACK);
00203     uLCD.printf("Press 3 to play");
00204     //start tone
00205     amp.PlayNote(700.0,0.2,0.5);
00206     amp.PlayNote(1000.0,0.2,0.0);
00207     amp.PlayNote(600.0,0.1,0.5);
00208     amp.PlayNote(1000.0,0.1,0.0);
00209     amp.PlayNote(700.0,0.5,0.5);
00210     //wait until 3 is pressed
00211     while(key != 3+1);
00212     //wait until 3 is unpressed
00213     while(key == 3+1);
00214     //clear screen
00215     uLCD.cls();
00216     uLCD.textbackground_color(BLACK);
00217     uLCD.background_color(BLACK);
00218     //setup initial state for every object
00219     curr_x_u = 9;
00220     curr_y_u = 63;
00221     curr_x_b = 25;
00222     curr_y_b = 63;
00223     curr_x_e = 75;
00224     curr_y_e = 75;
00225     dx_e = -0.75;
00226     dy_e = 0.75;
00227     curr_x_p = 110;
00228     curr_y_p = 15;
00229     old_x_p = curr_x_p;
00230     old_y_p = curr_y_p;
00231     //draw everything
00232     uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
00233     uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
00234     uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
00235     uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
00236     uLCD.color(WHITE);
00237     uLCD.locate(1,2);
00238     uLCD.printf("Place ball!");
00239     //logic for placing ball before game starts based on key presses; prevents ball from going through walls & middle boundary
00240     while(key != 3+1) {
00241         old_x_e = curr_x_e;
00242         old_y_e = curr_y_e;
00243         if (key==0+1) {
00244             curr_x_e-=2;
00245             if (curr_x_e < 71) curr_x_e = 71;
00246         }
00247         if (key==8+1) {
00248             curr_x_e+=2;
00249             if (curr_x_e > 120) curr_x_e = 120;
00250         }
00251         if (key==4+1) {
00252             curr_y_e+=2;
00253             if (curr_y_e > 120) curr_y_e = 120;
00254         }
00255         if (key==5+1) {
00256             curr_y_e-=2;
00257             if (curr_y_e < 7) curr_y_e = 7;
00258         }
00259         //draw ball
00260         uLCD.locate(1,2);
00261         uLCD.printf("Place ball!");
00262         uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
00263         uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
00264         uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
00265     }
00266     //wait until 3 is unpressed
00267     while(key==3+1);
00268     uLCD.locate(1,2);
00269     uLCD.color(BLACK);
00270     uLCD.printf("Place ball!");
00271     fx_e = curr_x_e;
00272     fy_e = curr_y_e;
00273     uLCD.color(WHITE);
00274     //countdown til start
00275     for (int i=3; i>=1; i--) {
00276         uLCD.locate(1, 2);
00277         uLCD.printf("%2D", i);
00278         amp.PlayNote(800.0,0.025,0.5);
00279         wait(1);
00280     }
00281     uLCD.locate(1,2);
00282     uLCD.color(BLACK);
00283     uLCD.printf("  ");
00284     //game is live, start other two threads
00285     s = LIVE;
00286     box.start(box_thread);
00287     portal.start(portal_thread);
00288     //collision logic for angry ball
00289     //check if ball has collided with a wall, the user, or the payload (gameover)
00290     while(s == LIVE || s == PAUSE) {
00291         while (s==PAUSE);
00292         old_x_e = curr_x_e;
00293         old_y_e = curr_y_e;
00294         if (curr_x_e < 7) {
00295             curr_x_e = 7;
00296             dx_e = -dx_e;
00297         } else if (curr_x_e > 120) {
00298             curr_x_e = 120;
00299             dx_e = -dx_e;
00300         } else if (curr_y_e > 120) {
00301             curr_y_e = 120;
00302             dy_e = -dy_e;
00303         } else if (curr_y_e < 7) {
00304             curr_y_e = 7;
00305             dy_e = -dy_e;
00306         } else if (((dx_e < 0 && curr_x_e-6 < curr_x_u+6 && curr_x_e > curr_x_u) || (dx_e > 0 && curr_x_e+6 > curr_x_u-6 && curr_x_e < curr_x_u)) && ((curr_y_e+6 > curr_y_u-6 && curr_y_e < curr_y_u) || (curr_y_e-6 < curr_y_u+6 && curr_y_e > curr_y_u))) {
00307             dx_e = -dx_e;
00308         } else if (((dy_e < 0 && curr_y_e-6 < curr_y_u+6 && curr_y_e > curr_y_u) || (dy_e > 0 && curr_y_e+6 > curr_y_u-6 && curr_y_e < curr_y_u)) && ((curr_x_e+6 > curr_x_u-6 && curr_x_e < curr_x_u) || (curr_x_e-6 < curr_x_u+6 && curr_x_e > curr_x_u))) {
00309             dy_e = -dy_e;
00310         }
00311         if (((dx_e < 0 && curr_x_e-6 < curr_x_b+6 && curr_x_e > curr_x_b) || (dx_e > 0 && curr_x_e+6 > curr_x_b-6 && curr_x_e < curr_x_b)) && ((curr_y_e+6 > curr_y_b-6 && curr_y_e < curr_y_b) || (curr_y_e-6 < curr_y_b+6 && curr_y_e > curr_y_b))) {
00312             s = DEAD;
00313         } else if (((dy_e < 0 && curr_y_e-6 < curr_y_b+6 && curr_y_e > curr_y_b) || (dy_e > 0 && curr_y_e+6 > curr_y_b-6 && curr_y_e < curr_y_b)) && ((curr_x_e+6 > curr_x_b-6 && curr_x_e < curr_x_b) || (curr_x_e-6 < curr_x_b+6 && curr_x_e > curr_x_b))) {
00314             s = DEAD;
00315         }
00316         //update floating point coordinates with velocity values
00317         fx_e += dx_e;
00318         fy_e += dy_e;
00319         //cast floating coordinates to integer coordinates
00320         curr_x_e = (int) fx_e;
00321         curr_y_e = (int) fy_e;
00322         //redraw ball
00323         lcd_mutex.lock();
00324         uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
00325         uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
00326         lcd_mutex.unlock();
00327         //if ball-payload collision has occurred, game is now dead
00328         if (s == DEAD) {
00329             //print gameover, score, instructions to start over, and play gameover sound
00330             lcd_mutex.lock();
00331             uLCD.cls();
00332             uLCD.locate(1,2);
00333             uLCD.color(RED);
00334             uLCD.printf("GAMEOVER");
00335             uLCD.locate(1,4);
00336             uLCD.color(0x4B0082);
00337             uLCD.printf("Score:%d", score);
00338             uLCD.locate(1,8);
00339             uLCD.color(WHITE);
00340             uLCD.printf("Press 3 to retry");
00341             amp.PlayNote(300.0,0.5,0.5);
00342             amp.PlayNote(300.0,0.05,0.0);
00343             amp.PlayNote(280.0,0.5,0.5);
00344             amp.PlayNote(300.0,0.05,0.0);
00345             amp.PlayNote(260.0,1.0,0.5);
00346             //wait until 3 is pressed
00347             while(key != 3+1);
00348             //wait until 3 is unpressed
00349             while(key == 3+1);
00350             //reinitialize game state for new game; same code as above
00351             uLCD.cls();
00352             score = 0;
00353             oldscore = 0;
00354             curr_x_u = 9;
00355             curr_y_u = 63;
00356             curr_x_b = 25;
00357             curr_y_b = 63;
00358             curr_x_e = 75;
00359             curr_y_e = 75;
00360             dx_e = -0.75;
00361             dy_e = 0.75;
00362             curr_x_p = 110;
00363             curr_y_p = 15;
00364             old_x_p = curr_x_p;
00365             old_y_p = curr_y_p;
00366             uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
00367             uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
00368             uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
00369             uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
00370             uLCD.color(WHITE);
00371             uLCD.locate(1,2);
00372             uLCD.printf("Place ball!");
00373             while(key != 3+1) {
00374                 old_x_e = curr_x_e;
00375                 old_y_e = curr_y_e;
00376                 if (key==0+1) {
00377                     curr_x_e-=2;
00378                     if (curr_x_e < 71) curr_x_e = 71;
00379                 }
00380                 if (key==8+1) {
00381                     curr_x_e+=2;
00382                     if (curr_x_e > 120) curr_x_e = 120;
00383                 }
00384                 if (key==4+1) {
00385                     curr_y_e+=2;
00386                     if (curr_y_e > 120) curr_y_e = 120;
00387                 }
00388                 if (key==5+1) {
00389                     curr_y_e-=2;
00390                     if (curr_y_e < 7) curr_y_e = 7;
00391                 }
00392                 uLCD.locate(1,2);
00393                 uLCD.printf("Place ball!");
00394                 uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
00395                 uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
00396                 uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
00397             }
00398             while(key==3+1);
00399             uLCD.locate(1,2);
00400             uLCD.color(BLACK);
00401             uLCD.printf("Place ball!");
00402             fx_e = curr_x_e;
00403             fy_e = curr_y_e;
00404             uLCD.color(WHITE);
00405             for (int i=3; i>=1; i--) {
00406                 uLCD.locate(1, 2);
00407                 uLCD.printf("%2D", i);
00408                 amp.PlayNote(800.0,0.025,0.5);
00409                 wait(1);
00410             }
00411             uLCD.locate(1,2);
00412             uLCD.color(BLACK);
00413             uLCD.printf("  ");
00414             lcd_mutex.unlock();
00415             //game is live again
00416             //restart the other threads
00417             s = LIVE;
00418             box.start(box_thread);
00419             portal.start(portal_thread);
00420         }
00421     }
00422 }