Mini game developed for ECE 4180 lab
Dependencies: 4DGL-uLCD-SE mbed-rtos mbed
Diff: main.cpp
- Revision:
- 0:a358215f57b7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon Mar 14 17:42:07 2016 +0000 @@ -0,0 +1,436 @@ +// Cactus Jumper video game +// +#include "mbed.h" +#include "rtos.h" +#include "uLCD_4DGL.h" +#include "entity.h" + + +uLCD_4DGL uLCD(p9,p10,p11); // serial tx, serial rx, reset pin; +Mutex posMutex; +int state = STATE_START; +int score; +int k; +char *scCompBuf; +char *writtenScore; +char *scoreStr; +bool initialFlash = false; +bool scoreDrawn = false; +bool collided = false; +bool newHighScore = false; +bool titleDrawn = false; +char readchar = ' '; +class Nav_Switch +{ +public: + Nav_Switch(PinName up,PinName down,PinName left,PinName right,PinName fire); + int read(); +//boolean functions to test each switch + bool up(); + bool down(); + bool left(); + bool right(); + bool fire(); +//automatic read on RHS + operator int (); +//index to any switch array style + bool operator[](int index) { + return _pins[index]; + }; +private: + BusIn _pins; + +}; +Nav_Switch::Nav_Switch (PinName up,PinName down,PinName left,PinName right,PinName fire): + _pins(up, down, left, right, fire) +{ + _pins.mode(PullUp); //needed if pullups not on board or a bare nav switch is used - delete otherwise + //Thread::wait(1); //delays just a bit for pullups to pull inputs high + wait(0.001); +} +inline bool Nav_Switch::up() +{ + return !(_pins[0]); +} +inline bool Nav_Switch::down() +{ + return !(_pins[1]); +} +inline bool Nav_Switch::left() +{ + return !(_pins[2]); +} +inline bool Nav_Switch::right() +{ + return !(_pins[3]); +} +inline bool Nav_Switch::fire() +{ + return !(_pins[4]); +} +inline int Nav_Switch::read() +{ + return _pins.read(); +} +inline Nav_Switch::operator int () +{ + return _pins.read(); +} + +Nav_Switch myNav( p25, p22, p23, p21, p24); +int i = 111; //variable used for horizontal player movement +int j = 96; //variable used for vertical player movement +//initialize player and cursor +Player *player = new Player(); +Cursor *curs = new Cursor(); +//thread for player input detection +void fire_thread(void const *args) { + while(true) { + //if player moves to the right during gameplay + if(myNav.right() && state == STATE_GAME) { + posMutex.lock(); + i = ((i > 110) ? 110 : i + DELTA_X_P); + (*player).col = i; + (*player).hasMoved = true; + (*player).moveFlag = 2; + posMutex.unlock(); + Thread::wait(35); + } + //if player moves to the left during gameplay + if(myNav.left() && state == STATE_GAME) { + posMutex.lock(); + i = ((i < 2) ? 2 : i - DELTA_X_P); + (*player).col = i; + (*player).hasMoved = true; + (*player).moveFlag = 1; + posMutex.unlock(); + Thread::wait(35); + } + posMutex.lock(); + //if player jumps during gameplay and is not already in the jumping or falling state + if((myNav.up() && !((*player).isJumping || (*player).isFalling)) && state == STATE_GAME) { + (*player).isJumping = true; + (*player).upCounter = 48; + (*player).fallCounter = 48; + } + posMutex.unlock(); + //if fire is pressed on start screen, begin game + if(myNav.fire() && state != STATE_GAME && state != STATE_SCORE && state != STATE_LOSE) { + state = STATE_GAME; + Thread::wait(35); + } + //Go to either start screen or new height screen when fire is pressed on game over screen + if(myNav.fire() && state == STATE_LOSE) { + if(!newHighScore) { + titleDrawn = false; + state = STATE_START; + } else { + state = STATE_SCORE; + } + Thread::wait(500); + } + //Navigation controls for high score screen + //if fire is pressed on the new high score screen + if(myNav.fire() && state == STATE_SCORE) { + //check whether to enter an initial or begin writing initials to file + if((*curs).curChar >= 'A' && (*curs).curChar <= 'Z') { + (*curs).initBuf[(*curs).c] = (*curs).curChar; + } + (*curs).letterPressed = true; + Thread::wait(200); + } + //If up is pressed on the new high score screen + if(myNav.up() && state == STATE_SCORE) { + //make sure cursor would not go out of bounds if moved + if(!((*curs).curChar - 8 < 'A')) { + (*curs).row -= (*curs).delta_y; + (*curs).hasMoved = true; + (*curs).moveFlag = 4; + (*curs).curChar-=8; + } + Thread::wait(100); + } + //If down is pressed on the new high score screen + if(myNav.down() && state == STATE_SCORE) { + //make sure cursor would not go out of boudns if moved + if(!((*curs).curChar + 8 > '[')) { + (*curs).row += (*curs).delta_y; + (*curs).hasMoved = true; + (*curs).moveFlag = 8; + (*curs).curChar+=8; + } + Thread::wait(100); + } + //If left is pressed on the new high score screen + if(myNav.left() && state == STATE_SCORE) { + //make sure cursor would not go out of boudns if moved + if(!((*curs).curChar == 'A') && !((*curs).curChar == 'I') && !((*curs).curChar == 'Q') && !((*curs).curChar == 'Y')) { + (*curs).col -= (*curs).delta_x; + (*curs).hasMoved = true; + (*curs).moveFlag = 1; + (*curs).curChar--; + } + Thread::wait(100); + } + //If right is pressed on the new high score screen + if(myNav.right() && state == STATE_SCORE) { + //make sure cursor would not go out of boudns if moved + if(!((*curs).curChar == 'H') && !((*curs).curChar == 'P') && !((*curs).curChar == 'X') && !((*curs).curChar == '[')) { + (*curs).col += (*curs).delta_x; + (*curs).hasMoved = true; + (*curs).moveFlag = 2; + (*curs).curChar++; + } + Thread::wait(100); + } + } +} +int main() +{ + //create thread for handling user input + Thread thread(fire_thread); + //initialize screen pointer array; allocate screen objects in memory + Screen *screenP[2]; + screenP[0] = new Screen(0x780D,true,-127); + screenP[1] = new Screen(0x780D,true,0); + //set baud rate & initialize media on uLCD + uLCD.baudrate(3000000); + uLCD.media_init(); + while(true) { + if(!(state | STATE_START)) { + //start screen + if(!titleDrawn) { //we want to only draw this information once + newHighScore = false; + //initialize game information + initialize(screenP, player, &score, &initialFlash, &scoreDrawn, &collided, &i, &j, curs); + //draw title screen and text + uLCD.set_sector_address(0x001D, 0x78D9); + uLCD.display_image(0,0); + uLCD.textbackground_color(0x8f7738); + uLCD.locate(3,12); + uLCD.color(RED); + uLCD.printf("Press fire"); + uLCD.locate(4,13); + uLCD.printf("to start"); + uLCD.background_color(COVER_COLOR); + //read in high score to display + uLCD.set_sector_address(0x001D, 0x8700); + uLCD.locate(3,15); + readchar = ' '; + while(readchar != '\xFF') { + readchar = uLCD.read_byte(); + uLCD.putc(readchar); + } + } + titleDrawn = true; + score = 0; + //The remainder of this if() causes the "blinking" of the "Press fire to start" on the title screen + Thread::wait(250); + uLCD.locate(3,12); + uLCD.color(0x8f7738); + uLCD.printf("Press fire"); + uLCD.locate(4,13); + uLCD.printf("to start"); + Thread::wait(250); + uLCD.locate(3,12); + uLCD.color(RED); + uLCD.printf("Press fire"); + uLCD.locate(4,13); + uLCD.printf("to start"); + } else if(state & STATE_GAME) { + + if(!initialFlash) { + //we want to clear the screen and draw the background scenery only once when the game starts + uLCD.filled_rectangle(0,0,127,127,COVER_COLOR); + drawScenery(&uLCD); + initialFlash = true; + } + //draw the obstacles + drawScreens(&uLCD, screenP); + posMutex.lock(); + if((*player).isJumping || (*player).isFalling) { + //check if end conditions for each state is currently met + if(((*player).upCounter < 0) && ((*player).isJumping)) { + //if the player has reached the top of their jump + (*player).isJumping = false; + } + if(((*player).fallCounter < 0) && ((*player).isFalling)) { + //if the player has stopped falling + (*player).isFalling = false; + } + //check if the player can enter into a new state + if(!(*player).isJumping && ((*player).fallCounter > 0) && (!(*player).isFalling)) { + //set the falling condition for the player + (*player).isFalling = true; + } + //decrement jumping or falling counters, if applicable + if((*player).isFalling) { + (*player).row+=DELTA_Y; + (*player).fallCounter-=DELTA_Y; + } + if((*player).isJumping) { + (*player).row-=DELTA_Y; + (*player).upCounter-=DELTA_Y; + } + drawPlayerAirborneCoverUp(&uLCD, player); + if((*player).hasMoved) { + drawPlayerDiagCoverUp(&uLCD, player); + } + } + if((*player).hasMoved) { + //cover up residual pixels from previous location of the player if they have moved + //drawPlayerCoverUp(&uLCD, player); + drawPlayerCoverUp(&uLCD, player, (*player).moveFlag); + } + (*player).hasMoved = false; + (*player).moveFlag = 0; + //detect collision + CollisionDetect(screenP,player,&collided); + if(collided) { + //player has lost if collision detected + state = STATE_LOSE; + } + drawPlayer(&uLCD, player); + drawScore(&uLCD, score); + posMutex.unlock(); + //Evaluate screens, delete old off-screen screens if necessary, and update the score + evalScreens(screenP); + score++; + } else if((state & STATE_LOSE) >> 1) { + //draw game over screen, plus NEW HIGH SCORE text if applicable + uLCD.textbackground_color(0xf09860); + uLCD.locate(6,0); + uLCD.printf("GAME OVER "); + uLCD.set_sector_address(0x001D, 0x9300); + uLCD.locate(1,1); + k = 0; + if(scCompBuf != NULL) { + //delete the previous score that was checked if the game has run once before + delete scCompBuf; + } + scCompBuf = new char[15]; + readchar = ' '; + //read in the score from the memory location on the SD card + while(readchar != '\xFF') { + readchar = uLCD.read_byte(); + if(readchar >= '0' && readchar <= '9') { + scCompBuf[k] = readchar; + } + k++; + } + //determine if player should go to high score or title screen on fire + if(score > atoi(scCompBuf)) { + //new high score + newHighScore = true; + uLCD.locate(3,3); + uLCD.textbackground_color(COVER_COLOR); + uLCD.printf("NEW HIGH SCORE"); + } + } else if((state & STATE_SCORE) >> 2) { + //name entry for new high score + if(!scoreDrawn) { + //Only draw background pieces once + uLCD.filled_rectangle(0,0,127,127,COVER_COLOR); + uLCD.locate(0,0); + uLCD.textbackground_color(COVER_COLOR); + uLCD.printf("HIGH SCORE: %d\n\n", score); + uLCD.printf("Initials:\n\n"); + uLCD.locate(0,4); + //Letter select for initials + uLCD.printf(" A B C D E F G H \n\n"); + uLCD.printf(" I J K L M N O P \n\n"); + uLCD.printf(" Q R S T U V W X \n\n"); + uLCD.printf(" Y Z "); + uLCD.filled_rectangle(36,81,40,85,0x000000); + (*curs).draw(&uLCD); + scoreDrawn = true; + } + if((*curs).letterPressed) { + //draw initials + if((*curs).curChar >= 'A' && (*curs).curChar <= 'Z') { //If selected character is not the END character + //pick correct location on screen to draw initial + uLCD.locate(10+(*curs).c,2); + uLCD.printf("%c",(*curs).initBuf[(*curs).c]); + (*curs).c = (((*curs).c < 2) ? (*curs).c + 1 : 2); + (*curs).letterPressed = false; + } else { + //write score to file + if(writtenScore == NULL) { + //allocate array + writtenScore = new char[4 + 15]; + } else { + delete writtenScore; + writtenScore = new char[4 + 15]; + } + if(scoreStr != NULL) { + //delete any previous string representation of score + delete scoreStr; + } + scoreStr = new char[15]; + //write score to display into a character array + sprintf(writtenScore,"%s %d",(*curs).initBuf,score); + //overwrite 0 that would appear after initials on score screen with blank space + writtenScore[3] = ' '; + //set location of title screen display score + uLCD.set_sector_address(0x001D, 0x8700); + uLCD.locate(0,12); + uLCD.printf("\n\nStoring score..."); + for (int i=0; i<strlen(writtenScore); i++) { + uLCD.write_byte(writtenScore[i]); //write a byte to SD card + } + uLCD.flush_media(); + //set location for only the score + uLCD.set_sector_address(0x001D, 0x9300); + //read in score to character array + sprintf(scoreStr,"%d",score); + for (int i=0; i<strlen(scoreStr); i++) { + uLCD.write_byte(scoreStr[i]); //write a byte to SD card + } + //Reset game once score has been saved + titleDrawn = false; + state = STATE_START; + } + } + if((*curs).hasMoved) { + //cover up old cursor + (*curs).coverUp(&uLCD); + //draw new cursor + (*curs).draw(&uLCD); + (*curs).hasMoved = false; + (*curs).moveFlag = 0; + } + } + } +} + +void initialize(Screen **s, Player *p, int *score, bool* initFlash, bool *scDrawn, bool *collide, int *c, int *r, Cursor *curs) { + //initialize screen (only delete if the screen pointer is not a null pointer + if(s[0] != NULL) { + delete s[0]; + } + if(s[1] != NULL) { + delete s[1]; + } + s[0] = new Screen(0x780D,true,-127); + s[1] = new Screen(0x780D,true,0); + //Initialize or re-initialize player + if(p != NULL) { + delete p; + } + p = new Player(); + *score = 0; + //Initialize or re-initialize flags used during gameplay + *initFlash = false; + *scDrawn = false; + *collide = false; + *c = 111; + *r = 96; + //Initialize or re-initialize the cursor + if(curs != NULL) { + delete curs; + } + curs = new Cursor(); +} + + +