Simple implementation of Guitar Hero with mbed.
Dependencies: 4DGL-uLCD-SE MCP23S17 SDFileSystem mbed-rtos mbed wave_player
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 }
Generated on Wed Jul 27 2022 02:59:31 by 1.7.2