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)
{ }
}