Simple implementation of Guitar Hero with mbed.

Dependencies:   4DGL-uLCD-SE MCP23S17 SDFileSystem mbed-rtos mbed wave_player

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*===================================================================
00002 --------------------- Guitar Hero mbed Program ----------------------
00003 
00004 This program recreates the classic Guitar Hero game using the mbed
00005 processor, along with various other peripherals, such as capacitive
00006 touch sensors, an IR sensor, and a uLCD display.
00007 
00008 Written for:
00009     Georgia Institute of Technology
00010     ECE 4180, Lab 4, Section B
00011     Dr. James Hamblen
00012     
00013 Authors:
00014     Garren Boggs
00015     Anthony Jones
00016 ===================================================================*/
00017 
00018 #include "mbed.h"
00019 #include "rtos.h" //For threading
00020 #include "uLCD_4DGL.h" //Display
00021 #include "SDFileSystem.h" //SD card functionality
00022 #include "wave_player.h" //For playing music
00023 #include "MCP23S17.h" //Serial PC for debugging
00024 #include <mpr121.h> //Capacitive touch sensors
00025 #include <stdlib.h> 
00026 #include <time.h>
00027 
00028 #define NOTE_START 24
00029 #define NOTE_HEIGHT 9
00030 #define NOTE_WIDTH 27
00031 #define MISS 128
00032 #define SPEED 3
00033 #define HIT_TOP 98
00034 #define HIT_BOT 125
00035 #define VICTORY_THRESHOLD 50
00036 
00037 bool paused = false; //If paused, stop gameplay and show on screen
00038 bool gameOver = false; //Occurs if HP reaches zero
00039 bool victory = false;
00040 bool resetDisplay = false; //After resetting game or unpausing
00041 
00042 bool gHit = false;
00043 bool rHit = false;
00044 bool yHit = false;
00045 bool bHit = false;
00046 
00047 int hp = 260; //Hitpoint count
00048 int score = 0;   //Score count
00049 int combo = 0;   //Combo count
00050 int notesCreated = 0;   //Victory after threshold
00051     
00052 /*  Note locations. The values are the x and y positions respectively.
00053     A value of -1 for these coordinates indicates that the specific
00054     note is currently not on the screen. */
00055 int gNote[2]= {0,-1}; //Current GREEN note location. 
00056 int rNote[2]= {NOTE_WIDTH+1,-1}; //Current RED note location. 
00057 int yNote[2]= {2*NOTE_WIDTH+2,-1}; //Current YELLOW note location. 
00058 int bNote[2]= {3*NOTE_WIDTH+3,-1}; //Current BLUE note location.
00059 
00060 //-------------------------------------------------------------------
00061 /*  Instantiates the capacitive touch sensors. These will be used for
00062     determining which notes have been pressed by the user. */
00063     
00064 /*  Setup the i2c bus on pins 9 and 10 */
00065     I2C i2c(p9, p10);  
00066     
00067 /*  Setup the Mpr121: */  
00068     Mpr121 notes(&i2c, Mpr121::ADD_VSS);
00069 //-------------------------------------------------------------------
00070 
00071 //-------------------------------------------------------------------
00072 /*  Create an interrupt receiver to detect when the user has
00073     strummed. */
00074     InterruptIn interruptStrum(p26);
00075 //-------------------------------------------------------------------
00076 
00077 //-------------------------------------------------------------------
00078 /*  Initialize SD Card Reader to import song to be played during the
00079     game. */
00080     SDFileSystem sd(p5, p6, p7, p8, "sd");
00081     
00082 /*  Initializes the wave player to actually play the song. */
00083     AnalogOut DACout (p18);
00084     wave_player player(&DACout);
00085 //-------------------------------------------------------------------
00086 
00087 //-------------------------------------------------------------------
00088 /*  Initializes the uLCD display as a UI for the game */
00089     uLCD_4DGL uLCD(p28,p27,p30);
00090     
00091 /*  Create an interrupt to allow the user to pause the game, this
00092     freezing the game UI. */
00093     InterruptIn interruptPause(p16);
00094 //-------------------------------------------------------------------
00095 
00096 //-------------------------------------------------------------------
00097 /*  Initialize the mbed LEDs for testing and debugging purposes. */
00098     DigitalOut led1(LED1);
00099     DigitalOut led2(LED2);
00100     DigitalOut led3(LED3);
00101     DigitalOut led4(LED4);
00102     
00103 /*  Setup the Serial to the PC for debugging */
00104     Serial pc(USBTX, USBRX);
00105     DigitalIn test(p24);
00106 //-------------------------------------------------------------------   
00107 
00108 /*
00109 *   Detects when the user has strum in order to play a note. This occurs
00110 *   when the user waves a hand close to the IR sensor. If close enough,
00111 *   the defined threshold will be passes and registered as a strum.
00112 */
00113 void strumInterrupt() 
00114 {
00115     //For resetting a game
00116     if(gameOver || victory)
00117     {
00118         gNote[1] = -1;
00119         rNote[1] = -1;
00120         yNote[1] = -1;
00121         bNote[1] = -1;
00122         notesCreated = 0;
00123         score = 0;
00124         combo = 0;
00125         hp = 260;
00126         
00127         gHit = false;
00128         rHit = false;
00129         yHit = false;
00130         bHit = false;
00131         
00132         resetDisplay = true;
00133         paused = false;
00134         gameOver = false;
00135         victory = false;
00136         return;        
00137     }
00138     
00139     //Read values of selected notes off of capacitive touch sensor
00140     int value = notes.read(0x00);
00141     value += notes.read(0x01)<<8;    
00142     
00143     //Determine if a note or combination of notes is being pressed
00144     if((value == 1 || value == 3 || value == 5 || value == 7 || value == 9 || value == 11 || value == 13 || value == 15) && 
00145         gNote[1] < HIT_BOT && gNote[1] > HIT_TOP)
00146         gHit = true;
00147     if((value == 2 || value == 3 || value == 6 || value == 7 || value == 10 || value == 11 || value == 14 || value == 15) && 
00148         rNote[1] < HIT_BOT && rNote[1] > HIT_TOP)
00149         rHit = true;
00150     if((value == 4 || value == 5 || value == 6 || value == 7 || value == 12 || value == 13 || value == 14 || value == 15) && 
00151         yNote[1] < HIT_BOT && yNote[1] > HIT_TOP)
00152         yHit = true;
00153     if((value == 8 || value == 9 || value == 10 || value == 11 || value == 12 || value == 13 || value == 14 || value == 15) && 
00154         bNote[1] < HIT_BOT && bNote[1] > HIT_TOP)
00155         bHit = true;
00156     
00157 }
00158 
00159 /*
00160 *   Updates the game display's life bar counter. If life bar drops below zero,
00161 *   game ends.
00162 */
00163 void updateLifeBar()
00164 {
00165     int i;
00166     int offset = 0;
00167     
00168     //Creates life bars on the game UI for the user to know how well they are doing.
00169     //If the user loses HP, the bars will decrease, and as the user repleneshes their
00170     //HP, the bars will begin to rise.
00171     for(i = 0; i < 8; i++)
00172     {
00173         if(hp < (260 - i*10))
00174             uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,BLACK);  
00175         else
00176             uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,GREEN);
00177         offset += 2;
00178     }
00179     for(i = 8; i < 19; i++)
00180     {
00181         if(hp < (260 - i*10))
00182             uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,BLACK);
00183         else
00184             uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,YELLOW);
00185         offset += 2;
00186     }
00187     for(i = 19; i < 26; i++)
00188     {
00189         if(hp < (260 - i*10))
00190             uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,BLACK);
00191         else
00192             uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,RED);
00193         offset += 2;
00194     }
00195 }
00196 
00197 /*
00198 *   Initializes the game UI with the predefined background. There are
00199 *   four specific notes the user can play, and thus there will be four
00200 *   distinct lanes that are drawn initially.
00201 */
00202 void setupDisplay()
00203 {
00204     //Background display
00205     uLCD.cls();
00206     uLCD.background_color(BLACK);
00207     uLCD.line(NOTE_WIDTH,NOTE_START-4,NOTE_WIDTH,127,WHITE);
00208     uLCD.line(2*NOTE_WIDTH+1,NOTE_START-4,2*NOTE_WIDTH+1,127,WHITE);
00209     uLCD.line(3*NOTE_WIDTH+2,NOTE_START-4,3*NOTE_WIDTH+2,127,WHITE);
00210     uLCD.filled_rectangle(4*NOTE_WIDTH+3,0,4*NOTE_WIDTH+4,127,WHITE);
00211     uLCD.filled_rectangle(126,0,127,127,WHITE);
00212     uLCD.line(0,NOTE_START-4,4*NOTE_WIDTH+3,NOTE_START-4,WHITE);
00213     uLCD.rectangle(0,127-(NOTE_HEIGHT+2),NOTE_WIDTH-1,127,GREEN);
00214     uLCD.rectangle(NOTE_WIDTH+1,127-(NOTE_HEIGHT+2),2*NOTE_WIDTH,127,RED);
00215     uLCD.rectangle(2*NOTE_WIDTH+2,127-(NOTE_HEIGHT+2),3*NOTE_WIDTH+1,127,YELLOW);
00216     uLCD.rectangle(3*NOTE_WIDTH+3,127-(NOTE_HEIGHT+2),4*NOTE_WIDTH+2,127,BLUE);
00217    
00218     //Scoreboard
00219     uLCD.printf("SCORE: %d\nCOMBO: %d",score,combo);
00220     
00221     //Initialize life bar
00222     updateLifeBar();
00223 }
00224 
00225 /*
00226 *   Calculates the dimensions for a specific note, based on its 
00227 *   current position.
00228 */
00229 void calculateNoteDims(int pos[], int dims[])
00230 {
00231     //If currently not on screen, do nothing.
00232     if(pos[1] < 0 || pos[1] > MISS)
00233     {
00234         dims[0] = 0;
00235         dims[1] = 0;
00236         dims[2] = 0;
00237         dims[3] = 0;
00238         return;
00239     }
00240     
00241     //Otherwise calculate dimensions    
00242     dims[0] = pos[0];   //x-left dimension
00243     dims[2] = pos[0] + NOTE_WIDTH - 1;  //x-right dimension
00244     
00245     dims[1] = (pos[1] - (NOTE_HEIGHT - 1)) > 0 ? pos[1] : 0;   //y-bottom dimension
00246     dims[3] = (pos[1] - (NOTE_HEIGHT - 1)) > 0 ? dims[1] + (NOTE_HEIGHT - 1) : pos[1]; //y-top dimension
00247 
00248     return;
00249 }
00250 
00251 /*
00252 *   Pauses the game.
00253 */
00254 void pauseGame()
00255 {
00256     if(paused)
00257     {
00258         resetDisplay = true;
00259     }
00260     paused = !paused;
00261     wait(0.2);   
00262 }
00263 
00264 /*
00265 *   The game loop. This continuously updates the display with the notes, and
00266 *   acts accordingly if the user has strum, and a note has been hit. If the
00267 *   game is paused, the notes will cease movement, and a text displaying so
00268 *   will appear on the screen.
00269 */
00270 void playGame(void const *args)
00271 {
00272     int gDims[4];
00273     int rDims[4];
00274     int yDims[4];
00275     int bDims[4];
00276     
00277     while(1)
00278     {
00279         if(!gameOver && !victory && !paused)
00280         {
00281             //Resets display if game has been reset or unpaused
00282             if(resetDisplay)
00283             {
00284                 resetDisplay = false;
00285                 setupDisplay();
00286             }
00287                 
00288             //Update life points
00289             updateLifeBar();
00290             
00291             //Calculate respective positions and draw accordingly if notes
00292             //are going on or off screen.
00293             calculateNoteDims(gNote, gDims);
00294             calculateNoteDims(rNote, rDims);
00295             calculateNoteDims(yNote, yDims);
00296             calculateNoteDims(bNote, bDims);
00297 
00298             //Draw the notes and erase old positions
00299             if(gNote[1] >= 0 && gNote[1] < MISS)
00300             {
00301                 if(gHit)
00302                 {
00303                     gHit = false;
00304                     uLCD.filled_rectangle(gNote[0],gNote[1]-SPEED,gNote[0]+NOTE_WIDTH-1,gNote[1]-1,BLACK);
00305                     uLCD.filled_rectangle(gDims[0],gDims[1],gDims[2],gDims[3],BLACK);
00306                     gNote[1] = -1;
00307                     score += 30;
00308                     combo++;
00309                     hp = (hp + (combo >= 10 ? 10 : combo)) >= 260 ? 260 : (hp + (combo >= 10 ? 10 : combo));
00310                 }
00311                 else
00312                 {
00313                     uLCD.filled_rectangle(gNote[0],gNote[1]-SPEED,gNote[0]+NOTE_WIDTH-1,gNote[1]-1,BLACK);
00314                     uLCD.filled_rectangle(gDims[0],gDims[1],gDims[2],gDims[3],GREEN);
00315                 }
00316             }
00317             
00318             if(rNote[1] >= 0 && rNote[1] < MISS)
00319             {
00320                 if(rHit)
00321                 {
00322                     rHit = false;
00323                     uLCD.filled_rectangle(rNote[0],rNote[1]-SPEED,rNote[0]+NOTE_WIDTH-1,rNote[1]-1,BLACK);
00324                     uLCD.filled_rectangle(rDims[0],rDims[1],rDims[2],rDims[3],BLACK);
00325                     rNote[1] = -1;
00326                     score += 30;
00327                     combo++;
00328                     hp = (hp + (combo >= 10 ? 10 : combo)) >= 260 ? 260 : (hp + (combo >= 10 ? 10 : combo));
00329                 }
00330                 else
00331                 {
00332                     uLCD.filled_rectangle(rNote[0],rNote[1]-SPEED,rNote[0]+NOTE_WIDTH-1,rNote[1]-1,BLACK);
00333                     uLCD.filled_rectangle(rDims[0],rDims[1],rDims[2],rDims[3],RED);
00334                 }
00335             }
00336             
00337             if(yNote[1] >= 0 && yNote[1] < MISS)
00338             {
00339                 if(yHit)
00340                 {
00341                     yHit = false;
00342                     uLCD.filled_rectangle(yNote[0],yNote[1]-SPEED,yNote[0]+NOTE_WIDTH-1,yNote[1]-1,BLACK);
00343                     uLCD.filled_rectangle(yDims[0],yDims[1],yDims[2],yDims[3],BLACK);
00344                     yNote[1] = -1;
00345                     score += 30;
00346                     combo++;
00347                     hp = (hp + (combo >= 10 ? 10 : combo)) >= 260 ? 260 : (hp + (combo >= 10 ? 10 : combo));
00348                 }
00349                 else
00350                 {
00351                     uLCD.filled_rectangle(yNote[0],yNote[1]-SPEED,yNote[0]+NOTE_WIDTH-1,yNote[1]-1,BLACK);
00352                     uLCD.filled_rectangle(yDims[0],yDims[1],yDims[2],yDims[3],YELLOW);
00353                 }
00354             }
00355             
00356             if(bNote[1] >= 0 && bNote[1] < MISS)
00357             {
00358                 if(bHit)
00359                 {
00360                     bHit = false;
00361                     uLCD.filled_rectangle(bNote[0],bNote[1]-SPEED,bNote[0]+NOTE_WIDTH-1,bNote[1]-1,BLACK);
00362                     uLCD.filled_rectangle(bDims[0],bDims[1],bDims[2],bDims[3],BLACK);
00363                     bNote[1] = -1;
00364                     score += 30;
00365                     combo++;
00366                     hp = (hp + (combo >= 10 ? 10 : combo)) >= 260 ? 260 : (hp + (combo >= 10 ? 10 : combo));
00367                 }
00368                 else
00369                 {
00370                     uLCD.filled_rectangle(bNote[0],bNote[1]-SPEED,bNote[0]+NOTE_WIDTH-1,bNote[1]-1,BLACK);
00371                     uLCD.filled_rectangle(bDims[0],bDims[1],bDims[2],bDims[3],BLUE); 
00372                 }
00373             
00374             }
00375             
00376             //Redraw target boxes at bottom of screen, incase notes are drawn over them
00377             uLCD.rectangle(0,127-(NOTE_HEIGHT+2),NOTE_WIDTH-1,127,GREEN);
00378             uLCD.rectangle(NOTE_WIDTH+1,127-(NOTE_HEIGHT+2),2*NOTE_WIDTH,127,RED);
00379             uLCD.rectangle(2*NOTE_WIDTH+2,127-(NOTE_HEIGHT+2),3*NOTE_WIDTH+1,127,YELLOW);
00380             uLCD.rectangle(3*NOTE_WIDTH+3,127-(NOTE_HEIGHT+2),4*NOTE_WIDTH+2,127,BLUE);
00381             
00382             //Check if each note is still on the screen. If it is, increment the position
00383             gNote[1] = gNote[1] < 0 ? (rand()%100 > 95 ? NOTE_START : -2) : gNote[1] >= MISS ? -1 : (gNote[1] + SPEED);
00384             if(gNote[1] == -1)
00385             {
00386                hp -= 10;
00387                combo = 0; 
00388             }
00389             if(gNote[1] == NOTE_START)
00390             {
00391                 notesCreated++;
00392             }
00393             
00394             rNote[1] = rNote[1] < 0 ? (rand()%100 > 95 ? NOTE_START : -2) : rNote[1] >= MISS ? -1 : (rNote[1] + SPEED); 
00395             if(rNote[1] == -1)
00396             {
00397                hp -= 10;
00398                combo = 0; 
00399             }
00400             if(rNote[1] == NOTE_START)
00401             {
00402                 notesCreated++;
00403             }
00404             
00405             yNote[1] = yNote[1] < 0 ? (rand()%100 > 95 ? NOTE_START : -2) : yNote[1] >= MISS ? -1 : (yNote[1] + SPEED); 
00406             if(yNote[1] == -1)
00407             {
00408                hp -= 10;
00409                combo = 0; 
00410             }
00411             if(yNote[1] == NOTE_START)
00412             {
00413                 notesCreated++;
00414             }
00415             
00416             bNote[1] = bNote[1] < 0 ? (rand()%100 > 95 ? NOTE_START : -2) : bNote[1] >= MISS ? -1 : (bNote[1] + SPEED);
00417             if(bNote[1] == -1)
00418             {
00419                hp -= 10;
00420                combo = 0; 
00421             }  
00422             if(bNote[1] == NOTE_START)
00423             {
00424                 notesCreated++;
00425             }
00426             
00427             //Check to make sure HP is above zero, otherwise, Game Over.
00428             if(hp <= 0)
00429                 gameOver = true;
00430                 
00431             //Check to see if user has won.
00432             if(notesCreated >= VICTORY_THRESHOLD)
00433                 victory = true;
00434 
00435             uLCD.locate(0,0);
00436             uLCD.printf("SCORE: %d\nCOMBO: %d",score,combo);
00437         }
00438         else if(gameOver)
00439         {
00440             //Sets up the display for a game over.
00441             uLCD.cls();
00442             uLCD.background_color(BLACK);
00443             uLCD.text_width(2);
00444             uLCD.text_height(2);
00445             uLCD.locate(0,0);
00446             uLCD.printf("\n\nGAME OVER");
00447             uLCD.locate(0,6);
00448             uLCD.text_width(1.5);
00449             uLCD.text_height(1.5);
00450             uLCD.printf("\n\r     Score: %d\n\r     Combo: %d",score,combo);
00451             uLCD.text_width(1);
00452             uLCD.text_height(1);
00453             uLCD.locate(0,11);
00454             uLCD.printf("\n\r   Strum to reset");
00455             uLCD.locate(0,0);
00456             while(gameOver)
00457             {
00458                 wait(0.1);
00459             }
00460         }
00461         else if(victory)
00462         {
00463             //Sets up the display for a victory.
00464             uLCD.cls();
00465             uLCD.background_color(BLACK);
00466             uLCD.text_width(2);
00467             uLCD.text_height(2);
00468             uLCD.locate(0,0);
00469             uLCD.printf("\n\nYOU ROCK!");
00470             uLCD.locate(0,6);
00471             uLCD.text_width(1.5);
00472             uLCD.text_height(1.5);
00473             uLCD.printf("\n\r     Score: %d\n\r     Combo: %d",score,combo);
00474             uLCD.text_width(1);
00475             uLCD.text_height(1);
00476             uLCD.locate(0,11);
00477             uLCD.printf("\n\r   Strum to reset");
00478             uLCD.locate(0,0);
00479             while(victory)
00480             {
00481                 wait(0.1);
00482             }
00483         }
00484         else
00485         {
00486             //Sets up the display for a paused game.
00487             uLCD.cls();
00488             uLCD.background_color(BLACK);
00489             uLCD.text_width(2);
00490             uLCD.text_height(2);
00491             uLCD.locate(0,0);
00492             uLCD.printf("\n\n\n PAUSED!");
00493             uLCD.text_width(1);
00494             uLCD.text_height(1);
00495             uLCD.locate(0,0);
00496             while(paused)
00497             {
00498                 wait(0.1);
00499             }
00500         }
00501     }
00502 }
00503 
00504 int main() 
00505 {   
00506     //Intialize display to show game UI
00507     setupDisplay();
00508     
00509     //Initialize the strum interrupt to act on a rising edge
00510     interruptStrum.rise(&strumInterrupt);
00511     
00512     //Initialize the pause interrupt
00513     interruptPause.rise(&pauseGame);
00514     
00515     //Initialize game loop
00516     Thread gl(playGame);
00517 
00518     //Play the song
00519     FILE *fp = fopen("/sd/sound/smoke_on_the_water.wav", "r");
00520     if(fp == NULL)
00521     {
00522         pc.printf("SD Card could not be read!");   
00523     }
00524     else
00525     {
00526    //     player.play(fp);
00527         fclose(fp);
00528     }
00529         
00530     //Tasks for main loop (mainly debugging)
00531     while(1)
00532     {    }
00533 }