Simple implementation of Guitar Hero with mbed.
Dependencies: 4DGL-uLCD-SE MCP23S17 SDFileSystem mbed-rtos mbed wave_player
main.cpp
- Committer:
- gboggs3
- Date:
- 2016-03-14
- Revision:
- 0:58d424fe40b9
File content as of revision 0:58d424fe40b9:
/*=================================================================== --------------------- Guitar Hero mbed Program ---------------------- This program recreates the classic Guitar Hero game using the mbed processor, along with various other peripherals, such as capacitive touch sensors, an IR sensor, and a uLCD display. Written for: Georgia Institute of Technology ECE 4180, Lab 4, Section B Dr. James Hamblen Authors: Garren Boggs Anthony Jones ===================================================================*/ #include "mbed.h" #include "rtos.h" //For threading #include "uLCD_4DGL.h" //Display #include "SDFileSystem.h" //SD card functionality #include "wave_player.h" //For playing music #include "MCP23S17.h" //Serial PC for debugging #include <mpr121.h> //Capacitive touch sensors #include <stdlib.h> #include <time.h> #define NOTE_START 24 #define NOTE_HEIGHT 9 #define NOTE_WIDTH 27 #define MISS 128 #define SPEED 3 #define HIT_TOP 98 #define HIT_BOT 125 #define VICTORY_THRESHOLD 50 bool paused = false; //If paused, stop gameplay and show on screen bool gameOver = false; //Occurs if HP reaches zero bool victory = false; bool resetDisplay = false; //After resetting game or unpausing bool gHit = false; bool rHit = false; bool yHit = false; bool bHit = false; int hp = 260; //Hitpoint count int score = 0; //Score count int combo = 0; //Combo count int notesCreated = 0; //Victory after threshold /* Note locations. The values are the x and y positions respectively. A value of -1 for these coordinates indicates that the specific note is currently not on the screen. */ int gNote[2]= {0,-1}; //Current GREEN note location. int rNote[2]= {NOTE_WIDTH+1,-1}; //Current RED note location. int yNote[2]= {2*NOTE_WIDTH+2,-1}; //Current YELLOW note location. int bNote[2]= {3*NOTE_WIDTH+3,-1}; //Current BLUE note location. //------------------------------------------------------------------- /* Instantiates the capacitive touch sensors. These will be used for determining which notes have been pressed by the user. */ /* Setup the i2c bus on pins 9 and 10 */ I2C i2c(p9, p10); /* Setup the Mpr121: */ Mpr121 notes(&i2c, Mpr121::ADD_VSS); //------------------------------------------------------------------- //------------------------------------------------------------------- /* Create an interrupt receiver to detect when the user has strummed. */ InterruptIn interruptStrum(p26); //------------------------------------------------------------------- //------------------------------------------------------------------- /* Initialize SD Card Reader to import song to be played during the game. */ SDFileSystem sd(p5, p6, p7, p8, "sd"); /* Initializes the wave player to actually play the song. */ AnalogOut DACout (p18); wave_player player(&DACout); //------------------------------------------------------------------- //------------------------------------------------------------------- /* Initializes the uLCD display as a UI for the game */ uLCD_4DGL uLCD(p28,p27,p30); /* Create an interrupt to allow the user to pause the game, this freezing the game UI. */ InterruptIn interruptPause(p16); //------------------------------------------------------------------- //------------------------------------------------------------------- /* Initialize the mbed LEDs for testing and debugging purposes. */ DigitalOut led1(LED1); DigitalOut led2(LED2); DigitalOut led3(LED3); DigitalOut led4(LED4); /* Setup the Serial to the PC for debugging */ Serial pc(USBTX, USBRX); DigitalIn test(p24); //------------------------------------------------------------------- /* * Detects when the user has strum in order to play a note. This occurs * when the user waves a hand close to the IR sensor. If close enough, * the defined threshold will be passes and registered as a strum. */ void strumInterrupt() { //For resetting a game if(gameOver || victory) { gNote[1] = -1; rNote[1] = -1; yNote[1] = -1; bNote[1] = -1; notesCreated = 0; score = 0; combo = 0; hp = 260; gHit = false; rHit = false; yHit = false; bHit = false; resetDisplay = true; paused = false; gameOver = false; victory = false; return; } //Read values of selected notes off of capacitive touch sensor int value = notes.read(0x00); value += notes.read(0x01)<<8; //Determine if a note or combination of notes is being pressed if((value == 1 || value == 3 || value == 5 || value == 7 || value == 9 || value == 11 || value == 13 || value == 15) && gNote[1] < HIT_BOT && gNote[1] > HIT_TOP) gHit = true; if((value == 2 || value == 3 || value == 6 || value == 7 || value == 10 || value == 11 || value == 14 || value == 15) && rNote[1] < HIT_BOT && rNote[1] > HIT_TOP) rHit = true; if((value == 4 || value == 5 || value == 6 || value == 7 || value == 12 || value == 13 || value == 14 || value == 15) && yNote[1] < HIT_BOT && yNote[1] > HIT_TOP) yHit = true; if((value == 8 || value == 9 || value == 10 || value == 11 || value == 12 || value == 13 || value == 14 || value == 15) && bNote[1] < HIT_BOT && bNote[1] > HIT_TOP) bHit = true; } /* * Updates the game display's life bar counter. If life bar drops below zero, * game ends. */ void updateLifeBar() { int i; int offset = 0; //Creates life bars on the game UI for the user to know how well they are doing. //If the user loses HP, the bars will decrease, and as the user repleneshes their //HP, the bars will begin to rise. for(i = 0; i < 8; i++) { if(hp < (260 - i*10)) uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,BLACK); else uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,GREEN); offset += 2; } for(i = 8; i < 19; i++) { if(hp < (260 - i*10)) uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,BLACK); else uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,YELLOW); offset += 2; } for(i = 19; i < 26; i++) { if(hp < (260 - i*10)) uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,BLACK); else uLCD.filled_rectangle(4*NOTE_WIDTH+6,3*i+offset,124,3*(i+1)+offset,RED); offset += 2; } } /* * Initializes the game UI with the predefined background. There are * four specific notes the user can play, and thus there will be four * distinct lanes that are drawn initially. */ void setupDisplay() { //Background display uLCD.cls(); uLCD.background_color(BLACK); uLCD.line(NOTE_WIDTH,NOTE_START-4,NOTE_WIDTH,127,WHITE); uLCD.line(2*NOTE_WIDTH+1,NOTE_START-4,2*NOTE_WIDTH+1,127,WHITE); uLCD.line(3*NOTE_WIDTH+2,NOTE_START-4,3*NOTE_WIDTH+2,127,WHITE); uLCD.filled_rectangle(4*NOTE_WIDTH+3,0,4*NOTE_WIDTH+4,127,WHITE); uLCD.filled_rectangle(126,0,127,127,WHITE); uLCD.line(0,NOTE_START-4,4*NOTE_WIDTH+3,NOTE_START-4,WHITE); uLCD.rectangle(0,127-(NOTE_HEIGHT+2),NOTE_WIDTH-1,127,GREEN); uLCD.rectangle(NOTE_WIDTH+1,127-(NOTE_HEIGHT+2),2*NOTE_WIDTH,127,RED); uLCD.rectangle(2*NOTE_WIDTH+2,127-(NOTE_HEIGHT+2),3*NOTE_WIDTH+1,127,YELLOW); uLCD.rectangle(3*NOTE_WIDTH+3,127-(NOTE_HEIGHT+2),4*NOTE_WIDTH+2,127,BLUE); //Scoreboard uLCD.printf("SCORE: %d\nCOMBO: %d",score,combo); //Initialize life bar updateLifeBar(); } /* * Calculates the dimensions for a specific note, based on its * current position. */ void calculateNoteDims(int pos[], int dims[]) { //If currently not on screen, do nothing. if(pos[1] < 0 || pos[1] > MISS) { dims[0] = 0; dims[1] = 0; dims[2] = 0; dims[3] = 0; return; } //Otherwise calculate dimensions dims[0] = pos[0]; //x-left dimension dims[2] = pos[0] + NOTE_WIDTH - 1; //x-right dimension dims[1] = (pos[1] - (NOTE_HEIGHT - 1)) > 0 ? pos[1] : 0; //y-bottom dimension dims[3] = (pos[1] - (NOTE_HEIGHT - 1)) > 0 ? dims[1] + (NOTE_HEIGHT - 1) : pos[1]; //y-top dimension return; } /* * Pauses the game. */ void pauseGame() { if(paused) { resetDisplay = true; } paused = !paused; wait(0.2); } /* * The game loop. This continuously updates the display with the notes, and * acts accordingly if the user has strum, and a note has been hit. If the * game is paused, the notes will cease movement, and a text displaying so * will appear on the screen. */ void playGame(void const *args) { int gDims[4]; int rDims[4]; int yDims[4]; int bDims[4]; while(1) { if(!gameOver && !victory && !paused) { //Resets display if game has been reset or unpaused if(resetDisplay) { resetDisplay = false; setupDisplay(); } //Update life points updateLifeBar(); //Calculate respective positions and draw accordingly if notes //are going on or off screen. calculateNoteDims(gNote, gDims); calculateNoteDims(rNote, rDims); calculateNoteDims(yNote, yDims); calculateNoteDims(bNote, bDims); //Draw the notes and erase old positions if(gNote[1] >= 0 && gNote[1] < MISS) { if(gHit) { gHit = false; uLCD.filled_rectangle(gNote[0],gNote[1]-SPEED,gNote[0]+NOTE_WIDTH-1,gNote[1]-1,BLACK); uLCD.filled_rectangle(gDims[0],gDims[1],gDims[2],gDims[3],BLACK); gNote[1] = -1; score += 30; combo++; hp = (hp + (combo >= 10 ? 10 : combo)) >= 260 ? 260 : (hp + (combo >= 10 ? 10 : combo)); } else { uLCD.filled_rectangle(gNote[0],gNote[1]-SPEED,gNote[0]+NOTE_WIDTH-1,gNote[1]-1,BLACK); uLCD.filled_rectangle(gDims[0],gDims[1],gDims[2],gDims[3],GREEN); } } if(rNote[1] >= 0 && rNote[1] < MISS) { if(rHit) { rHit = false; uLCD.filled_rectangle(rNote[0],rNote[1]-SPEED,rNote[0]+NOTE_WIDTH-1,rNote[1]-1,BLACK); uLCD.filled_rectangle(rDims[0],rDims[1],rDims[2],rDims[3],BLACK); rNote[1] = -1; score += 30; combo++; hp = (hp + (combo >= 10 ? 10 : combo)) >= 260 ? 260 : (hp + (combo >= 10 ? 10 : combo)); } else { uLCD.filled_rectangle(rNote[0],rNote[1]-SPEED,rNote[0]+NOTE_WIDTH-1,rNote[1]-1,BLACK); uLCD.filled_rectangle(rDims[0],rDims[1],rDims[2],rDims[3],RED); } } if(yNote[1] >= 0 && yNote[1] < MISS) { if(yHit) { yHit = false; uLCD.filled_rectangle(yNote[0],yNote[1]-SPEED,yNote[0]+NOTE_WIDTH-1,yNote[1]-1,BLACK); uLCD.filled_rectangle(yDims[0],yDims[1],yDims[2],yDims[3],BLACK); yNote[1] = -1; score += 30; combo++; hp = (hp + (combo >= 10 ? 10 : combo)) >= 260 ? 260 : (hp + (combo >= 10 ? 10 : combo)); } else { uLCD.filled_rectangle(yNote[0],yNote[1]-SPEED,yNote[0]+NOTE_WIDTH-1,yNote[1]-1,BLACK); uLCD.filled_rectangle(yDims[0],yDims[1],yDims[2],yDims[3],YELLOW); } } if(bNote[1] >= 0 && bNote[1] < MISS) { if(bHit) { bHit = false; uLCD.filled_rectangle(bNote[0],bNote[1]-SPEED,bNote[0]+NOTE_WIDTH-1,bNote[1]-1,BLACK); uLCD.filled_rectangle(bDims[0],bDims[1],bDims[2],bDims[3],BLACK); bNote[1] = -1; score += 30; combo++; hp = (hp + (combo >= 10 ? 10 : combo)) >= 260 ? 260 : (hp + (combo >= 10 ? 10 : combo)); } else { uLCD.filled_rectangle(bNote[0],bNote[1]-SPEED,bNote[0]+NOTE_WIDTH-1,bNote[1]-1,BLACK); uLCD.filled_rectangle(bDims[0],bDims[1],bDims[2],bDims[3],BLUE); } } //Redraw target boxes at bottom of screen, incase notes are drawn over them uLCD.rectangle(0,127-(NOTE_HEIGHT+2),NOTE_WIDTH-1,127,GREEN); uLCD.rectangle(NOTE_WIDTH+1,127-(NOTE_HEIGHT+2),2*NOTE_WIDTH,127,RED); uLCD.rectangle(2*NOTE_WIDTH+2,127-(NOTE_HEIGHT+2),3*NOTE_WIDTH+1,127,YELLOW); uLCD.rectangle(3*NOTE_WIDTH+3,127-(NOTE_HEIGHT+2),4*NOTE_WIDTH+2,127,BLUE); //Check if each note is still on the screen. If it is, increment the position gNote[1] = gNote[1] < 0 ? (rand()%100 > 95 ? NOTE_START : -2) : gNote[1] >= MISS ? -1 : (gNote[1] + SPEED); if(gNote[1] == -1) { hp -= 10; combo = 0; } if(gNote[1] == NOTE_START) { notesCreated++; } rNote[1] = rNote[1] < 0 ? (rand()%100 > 95 ? NOTE_START : -2) : rNote[1] >= MISS ? -1 : (rNote[1] + SPEED); if(rNote[1] == -1) { hp -= 10; combo = 0; } if(rNote[1] == NOTE_START) { notesCreated++; } yNote[1] = yNote[1] < 0 ? (rand()%100 > 95 ? NOTE_START : -2) : yNote[1] >= MISS ? -1 : (yNote[1] + SPEED); if(yNote[1] == -1) { hp -= 10; combo = 0; } if(yNote[1] == NOTE_START) { notesCreated++; } bNote[1] = bNote[1] < 0 ? (rand()%100 > 95 ? NOTE_START : -2) : bNote[1] >= MISS ? -1 : (bNote[1] + SPEED); if(bNote[1] == -1) { hp -= 10; combo = 0; } if(bNote[1] == NOTE_START) { notesCreated++; } //Check to make sure HP is above zero, otherwise, Game Over. if(hp <= 0) gameOver = true; //Check to see if user has won. if(notesCreated >= VICTORY_THRESHOLD) victory = true; uLCD.locate(0,0); uLCD.printf("SCORE: %d\nCOMBO: %d",score,combo); } else if(gameOver) { //Sets up the display for a game over. uLCD.cls(); uLCD.background_color(BLACK); uLCD.text_width(2); uLCD.text_height(2); uLCD.locate(0,0); uLCD.printf("\n\nGAME OVER"); uLCD.locate(0,6); uLCD.text_width(1.5); uLCD.text_height(1.5); uLCD.printf("\n\r Score: %d\n\r Combo: %d",score,combo); uLCD.text_width(1); uLCD.text_height(1); uLCD.locate(0,11); uLCD.printf("\n\r Strum to reset"); uLCD.locate(0,0); while(gameOver) { wait(0.1); } } else if(victory) { //Sets up the display for a victory. uLCD.cls(); uLCD.background_color(BLACK); uLCD.text_width(2); uLCD.text_height(2); uLCD.locate(0,0); uLCD.printf("\n\nYOU ROCK!"); uLCD.locate(0,6); uLCD.text_width(1.5); uLCD.text_height(1.5); uLCD.printf("\n\r Score: %d\n\r Combo: %d",score,combo); uLCD.text_width(1); uLCD.text_height(1); uLCD.locate(0,11); uLCD.printf("\n\r Strum to reset"); uLCD.locate(0,0); while(victory) { wait(0.1); } } else { //Sets up the display for a paused game. uLCD.cls(); uLCD.background_color(BLACK); uLCD.text_width(2); uLCD.text_height(2); uLCD.locate(0,0); uLCD.printf("\n\n\n PAUSED!"); uLCD.text_width(1); uLCD.text_height(1); uLCD.locate(0,0); while(paused) { wait(0.1); } } } } int main() { //Intialize display to show game UI setupDisplay(); //Initialize the strum interrupt to act on a rising edge interruptStrum.rise(&strumInterrupt); //Initialize the pause interrupt interruptPause.rise(&pauseGame); //Initialize game loop Thread gl(playGame); //Play the song FILE *fp = fopen("/sd/sound/smoke_on_the_water.wav", "r"); if(fp == NULL) { pc.printf("SD Card could not be read!"); } else { // player.play(fp); fclose(fp); } //Tasks for main loop (mainly debugging) while(1) { } }