Cactus Jumper
Description
This project uses the uLCDD 144-G2 LCD along with a navigation switch to create an obstacle-dodging game. The player controls the character on-screen to avoid the obstacles (with randomly-generated positions) to try and earn a high score. The game ends when the player makes contact with one of the obstacles. If the player has earned a new high score, they will be taken to the initial input screen, and their score and initials will be saved to the SD card so that their score will be displayed at the start menu. The game has separate threads for handling user input and displaying the on-screen graphics. The images used for the obstacles and character are stored on the microSD card on the uLCD.
Components
uLCD 144-G2
5-way Tactile Navigation Switch Breakout Board
Hookup Guide
Navigation Switch
| Mbed | NavSwitch |
|---|---|
| gnd | - |
| p21 | R |
| p22 | D |
| p23 | L |
| p24 | C |
| p25 | U |
| nc | + |
LCD Screen
| Mbed | uLCD cable |
|---|---|
| gnd | GND |
| VU | +5V |
| p9 | TX |
| p10 | RX |
| p11 | RES |
Image
Video
Code
Import programCactusJumper
Mini game developed for ECE 4180 lab
main.cpp
// 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();
}
entity.cpp
#include "entity.h"
void evalScreens(Screen **s) {
if((*s[1]).pos > 127) {
//delete screen, shift old screen 1 to screen 0, create new screen 1
delete s[1];
s[1] = s[0];
//s[0] = new Screen(0x780D, -127);
s[0] = new Screen(0x780D, true,-127);
}
(*s[0]).decScreen();
(*s[1]).decScreen();
}
void drawScreens(uLCD_4DGL *u, Screen **s) {
//Only draw left-most screen objects if it is not in its initial position
if((*s[0]).pos > -127) {
//overwrite previous location of blocks with bg color
for(int j = 0;j<=3;j++) {
DrawCoverUp((*(*s[0]).obs[j]).row, (*(*s[0]).obs[j]).col - DELTA_X,(*(*s[0]).obs[j]).w - 1,(*(*s[0]).obs[j]).h - 1,u);
}
//draw obstacles in new positions
DrawSliceBySlice((*s[0]).obs[0], u);
DrawSliceBySlice((*s[0]).obs[1], u);
DrawSliceBySlice((*s[0]).obs[2], u);
DrawSliceBySlice((*s[0]).obs[3], u);
}
//Since s[1] is always on-screen, draw its objects and any necessary cover-up boxes
for(int i = 0;i<=3;i++) {
DrawCoverUp((*(*s[1]).obs[i]).row, (*(*s[1]).obs[i]).col - DELTA_X,(*(*s[1]).obs[i]).w - 1, (*(*s[1]).obs[i]).h - 1,u);
}
//draw blocks in new positions
DrawSliceBySlice((*s[1]).obs[0], u);
DrawSliceBySlice((*s[1]).obs[1], u);
DrawSliceBySlice((*s[1]).obs[2], u);
DrawSliceBySlice((*s[1]).obs[3], u);
}
void drawPlayer(uLCD_4DGL *u, Player *p) {
//(*u).filled_rectangle((*p).col,(*p).row,(*p).col + 15,(*p).row + 31, (*p).color);
if((*p).isJumping) {
(*u).set_sector_address(0x001D, 0x78A5);
//set jumping sector
} else if((*p).isFalling) {
(*u).set_sector_address(0x001D, 0x78A2);
//set falling sector
} else {
(*u).set_sector_address(0x001D, 0x78A8);
}
//set neutral sector
(*u).display_image((*p).col, (*p).row);
}
void drawPlayerCoverUp(uLCD_4DGL *u, Player *p) {
//(*u).filled_rectangle((*p).col - (((*p).moveFlag & 2) >> 1) + ((*p).moveFlag & 1),(*p).row,(*p).col + 15,(*p).row + 31, 0x000000);
(*u).filled_rectangle((*p).col - (DELTA_X_P * (((*p).moveFlag & 2) >> 1)) + (((*p).w * ((*p).moveFlag & 1)) + 0),(*p).row,(*p).col - (1 * (((*p).moveFlag & 2) >> 1)) + ((16 * ((*p).moveFlag & 1)) + (DELTA_X_P * ((*p).moveFlag & 1))),(*p).row + 31, COVER_COLOR);
}
void drawPlayerCoverUp(uLCD_4DGL *u, Player *p, int dir) {
if((*p).moveFlag == MOVE_RIGHT) {
(*u).filled_rectangle((*p).col - DELTA_X_P - 1,(*p).row,(*p).col - 1,(*p).row + (*p).h - 1,COVER_COLOR);
}
if((*p).moveFlag == MOVE_LEFT) {
(*u).filled_rectangle((*p).col + (*p).w,(*p).row,(*p).col + (*p).w + DELTA_X_P + 1,(*p).row + (*p).h - 1,COVER_COLOR);
}
}
/*void drawPlayerAirborneCoverUp(uLCD_4DGL *u, Player *p) {
(*u).filled_rectangle((*p).col, (*p).row - (((*p).isFalling && ((*p).fallCounter >= 0)) ? DELTA_Y : 0) +
(((*p).isJumping && ((*p).upCounter >= 0)) ? 32 : 0),(*p).col + 15, (*p).row - (((*p).isFalling && ((*p).fallCounter >= 0)) ? 1 : 0) +
(((*p).isJumping && ((*p).upCounter >= 0)) ? 32 + DELTA_Y : 0), COVER_COLOR);
}*/
void drawPlayerAirborneCoverUp(uLCD_4DGL *u, Player *p) {
if((*p).isJumping) {
//draw a cover-up box below the player
(*u).filled_rectangle((*p).col,(*p).row + (*p).h,(*p).col + (*p).w - 1,(*p).row + (*p).h + DELTA_Y,COVER_COLOR);
}
if((*p).isFalling) {
//draw a cover-up box above the player
(*u).filled_rectangle((*p).col,(*p).row - DELTA_Y - 1, (*p).col + (*p).w - 1, (*p).row - 1,COVER_COLOR);
}
}
void drawPlayerDiagCoverUp(uLCD_4DGL *u, Player *p) {
if((*p).isJumping) {
if((*p).moveFlag == MOVE_RIGHT) {
(*u).filled_rectangle((*p).col - DELTA_X_P,(*p).row + (*p).h, (*p).col - 1, (*p).row + (*p).h +DELTA_Y - 1, COVER_COLOR);
}
if((*p).moveFlag == MOVE_LEFT) {
(*u).filled_rectangle((*p).col + (*p).w,(*p).row + (*p).h, (*p).col + (*p).w + DELTA_X_P - 1, (*p).row + (*p).h + DELTA_Y - 1, COVER_COLOR);
}
}
if((*p).isFalling) {
if((*p).moveFlag == MOVE_RIGHT) {
(*u).filled_rectangle((*p).col - DELTA_X_P,(*p).row - DELTA_Y,(*p).col - 1, (*p).row - 1,COVER_COLOR);
}
if((*p).moveFlag == MOVE_LEFT) {
(*u).filled_rectangle((*p).col + (*p).w, (*p).row - DELTA_Y, (*p).col + (*p).w + DELTA_X_P - 1, (*p).row - 1, COVER_COLOR);
}
}
}
void DrawIfOnScreen(Obstacle *o, uLCD_4DGL *u) {
if(((*o).col >= 0) || (((*o).col + (*o).w - 1) > 0)) { //If entity is on-screen
if((*o).col < 0) {
(*u).filled_rectangle(0,(*o).row,(*o).w - 1 + (*o).col,(*o).row + (*o).h - 1,(*o).color);//If left side is not on screen
} else if(((*o).col + 15) > 127) {
(*u).filled_rectangle((*o).col,(*o).row,127,(*o).row + (*o).h - 1,(*o).color);//Else if right side not on screen
} else {
(*u).filled_rectangle((*o).col,(*o).row,(*o).col + (*o).w - 1,(*o).row + (*o).h - 1,(*o).color); //Else all of the entity is on the screen
}
}
}
void DrawSliceBySlice(Obstacle *o, uLCD_4DGL *u) {
/*for(int i = 0; i<=15;i++) {
if((*o).col + i > 0 && (*o).col + i <= 127) {
(*u).set_sector_address(0x001D, 0x788F + i);
(*u).display_image((*o).col + i, (*o).row);
}
}*/
//(*u).set_sector_address(0x001D, 0x789F);
//(*u).display_image((*o).col, (*o).row);
if(((*o).col >= 0) || (((*o).col + (*o).w - 1) > 0)) { //If entity is on-screen
if((*o).col < 0) { //If the left side is off-screen
//set sector for particular slice
if((*o).w + (*o).col - 2 < 8) {
//+1 addition to pick sector
(*u).set_sector_address(0x001D, 0x78AB + (*o).w + (*o).col - 2);
} else {
//+2 addition to pick sector
(*u).set_sector_address(0x001D, 0x78B4 + ((((*o).w + (*o).col - 2) % 8) * 2));
}
//draw the object
(*u).display_image(0, (*o).row);
} else if(((*o).col + 15) > 127) { //If the right side is off-screen
if(127 - (*o).col < 8) {
//-1 to pick sector
(*u).set_sector_address(0x001D,0x78D8 - ((127 - (*o).col) % 8));
} else {
//-2 to pick sector
(*u).set_sector_address(0x001D,0x78D2 - (((127 - (*o).col) % 8) * 2));
}
//draw the object
(*u).display_image((*o).col, (*o).row);
} else { //Otherwise the entire object is on-screen
(*u).set_sector_address(0x001D, 0x789F);
(*u).display_image((*o).col, (*o).row);
}
}
}
void DrawCoverUp(int row, int col, int w, int h, uLCD_4DGL *u) {
if((col >= 0) || ((col + w) > 0)) { //If entity is on-screen
if(col < 0) {
//(*u).filled_rectangle(0,row,15+col,row + 15,0x000000);//If left side is not on screen
} else if((col + w) > 127) {
(*u).filled_rectangle(col,row,col + DELTA_X - 1,row + h,COVER_COLOR);//Else if right side not on screen
} else {
(*u).filled_rectangle(col,row,col + DELTA_X - 1,row + h,COVER_COLOR); //Else all of the entity is on the screen
}
}
}
void drawScenery(uLCD_4DGL *u) {
(*u).filled_rectangle(0,0,127,7,0xf09860);
(*u).filled_rectangle(0,8,127,15,0xf0a870);
(*u).filled_rectangle(0,16,127,23,0xf0b880);
(*u).filled_rectangle(0,24,127,31,0xf0c890);
(*u).set_sector_address(0x001D,0x791A);
(*u).display_image(0,0);
}
void drawScore(uLCD_4DGL *u, int score) {
char scoreChar[15];
sprintf(scoreChar,"%d",score);
(*u).textbackground_color(0xf09860);
(*u).locate(11-strlen(scoreChar),0);
(*u).printf("Score: %s",scoreChar);
}
//Collision detection functions
void CollisionDetect(Screen **s, Player *p,bool *flag) {
if((*s[0]).pos > -127) {
//Check if the player has collided with any of s[0]'s objects
for(int j = 0;j<=3;j++) {
if(detectTR((*s[0]).obs[j],p) || detectBL((*s[0]).obs[j],p) || detectBR((*s[0]).obs[j],p) || detectTL((*s[0]).obs[j],p)) {
//mark player as collided
//(*p).color = 0xFF00FF;
//mark block as collided
//(*(*s[0]).obs[j]).color = 0x00FFFF;
//set the collided flag to true
*flag = true;
}
}
}
//Check if the player has collided with any of s[1]'s objects
for(int i = 0;i<=3;i++) {
if(detectTR((*s[1]).obs[i],p) || detectBL((*s[1]).obs[i],p) || detectBR((*s[1]).obs[i],p) || detectTL((*s[1]).obs[i],p)) {
//mark player as collided
//(*p).color = 0xFF00FF;
//mark block as collided
//(*(*s[1]).obs[i]).color = 0x00FFFF;
//set the collided flag to true
*flag = true;
}
}
}
bool detectTR(Obstacle *o,Player *p) {
//return true if the top right corner of the player overlaps with an object
return ((((*o).col + (*o).w - 1) >= (*p).col) && (((*o).col + (*o).w - 1) <= (*p).col + (*p).w - 1)
&& (((*o).row >= (*p).row) && ((*o).row <= ((*p).row + (*p).h - 1))));
}
bool detectBL(Obstacle *o,Player *p) {
//return true if the bottom left corner of the player overlaps with an object
return ((((*o).col) >= (*p).col) && (((*o).col) <= (*p).col + (*p).w - 1)
&& (((*o).row + (*o).h - 1 >= (*p).row) && ((*o).row + (*o).h - 1 <= ((*p).row + (*p).h - 1))));
}
bool detectBR(Obstacle *o,Player *p) {
//return true if the bottom right corner of the player overlaps with an object
return ((((*o).col + (*o).w - 1) >= (*p).col) && (((*o).col + (*o).w - 1) <= (*p).col + (*p).w - 1)
&& (((*o).row + (*o).h - 1 >= (*p).row) && ((*o).row + (*o).h - 1 <= ((*p).row + (*p).h - 1))));
}
bool detectTL(Obstacle *o, Player *p) {
//return true if the top left corner of the player overlaps with an object
return ((((*o).col) >= (*p).col) && (((*o).col) <= (*p).col + (*p).w - 1)
&& (((*o).row >= (*p).row) && ((*o).row <= ((*p).row + (*p).h - 1))));
}
/*
//functions that belong to the Screen class
*/
void Screen::decScreen(void) {
//increment screen position and object positions so that they can move off-screen
pos+=DELTA_X;
(*obs[0]).col+=DELTA_X;
(*obs[1]).col+=DELTA_X;
(*obs[2]).col+=DELTA_X;
(*obs[3]).col+=DELTA_X;
//decrement position of objects associated with screen
}
Screen::Screen(int bg, int initPos) {
bgAddr = bg;
pos = initPos;
int j;
for(j = 0;j<=3;j++) {
obs[j] = new Obstacle(0x00FF00);
(*obs[j]).col = initPos + (16*j) + j;
(*obs[j]).row = 111;
}
}
//new default constructor
Screen::Screen(int bg, bool randomSpots, int initPos) {
pos = initPos;
for(int j = 0;j<=3;j++) {
obs[j] = new Obstacle(0x0000FF);
}
//Pick random locations within the screen for the objects
(*obs[0]).col = initPos + (rand() % 2) * 16;
(*obs[1]).col = ((rand() % 2) * 16) + (*obs[0]).col;
(*obs[2]).col = ((rand() % 2) * 16) + (*obs[1]).col;
(*obs[3]).col = ((rand() % 2) * 16) + (*obs[2]).col;
//set height to either 16 or 32 pixels for object
for(int j = 0;j<=3;j++) {
(*obs[j]).h = ((rand() % 2) == 1 ? H_32 : H_16);
(*obs[j]).w = H_16;
(*obs[j]).row = 127-(*obs[j]).h;
}
}
Screen::~Screen(void) {
//free the space for the deleted screen's obstacles in memory
delete obs[0];
delete obs[1];
delete obs[2];
delete obs[3];
}
/*
//Functions that belong to the Obstacle class
*/
Obstacle::Obstacle(int c) {
color = c;
w = 16;
h = 16;
row = 0;
col = 0;
}
/*
//Function that belong to the Player class
*/
Player::Player(void) {
//set default variables so that the player is in the bottom-right corner when the game begins
w = H_16;
h = H_32;
color = 0xFF0000; //color is deprecated, since we're using images now
col = 111;
row = 95;
hasMoved = false;
moveFlag = 0;
isFalling = false;
isJumping = false;
upCounter = -1;
fallCounter = -1;
}
/*
// Functions that belong to the cursor class
*/
Cursor::Cursor(void) {
//set default cursor position so that the cursor is over 'A' on the high score screen
row = 30;
col = 5;
s = 10;
curChar = 'A';
delta_x = 14;
delta_y = 16;
hasMoved = false;
letterPressed = false;
c = 0;
moveFlag = 0;
}
void Cursor::draw(uLCD_4DGL *u) {
//draw the cursor rectangle at the current position
(*u).rectangle(col,row,col + s,row + s,0xFF3300);
}
void Cursor::coverUp(uLCD_4DGL *u) {
//draw a rectangle with the BG color at the previous cursor position to cover up the rectangle left behind by previous draw function
(*u).rectangle(col + ((moveFlag & MOVE_LEFT) * ((delta_x - s) + s)) - (((moveFlag & MOVE_RIGHT) >> 1) * ((delta_x - s) + s)), row + (((moveFlag & MOVE_UP)>>2) * ((delta_y - s) + s)) - (((moveFlag & MOVE_DOWN)>>3) * ((delta_y - s) + s)),
(col + s) + ((moveFlag & MOVE_LEFT)* ((delta_x - s) + s)) - (((moveFlag & MOVE_RIGHT) >> 1) * ((delta_x - s) + s)), row + s + (((moveFlag & MOVE_UP)>>2) * ((delta_y - s) + s)) - (((moveFlag & MOVE_DOWN)>>3) * ((delta_y - s) + s)),
COVER_COLOR);
}
entity.h
#include "mbed.h"
#include "rtos.h"
#include "uLCD_4DGL.h"
#define DELTA_Y 3
#define DELTA_X 3
#define DELTA_X_P 2
#define COVER_COLOR 0xf0c890
#define H_32 32
#define H_16 16
#define STATE_START 0
#define STATE_GAME 1
#define STATE_LOSE 2
#define STATE_SCORE 4
#define MOVE_LEFT 1
#define MOVE_RIGHT 2
#define MOVE_UP 4
#define MOVE_DOWN 8
class Entity {
private:
public:
//y position of an entity (in pixels)
int row;
//x position of an entity (in pixels)
int col;
};
class Cursor : public Entity {
public:
//size of the s by s square cursor
int s;
//space that the cursor will move when the joystick is pressed left or right
int delta_x;
//space that the cursor will move when the joystick is pressed up or down
int delta_y;
//buffer that will contain the initials for a new high score
char initBuf[3];
int c;
//flag to see if the user moved the cursor
bool hasMoved;
//flag to see if the user has pressed the fire button on the high score screen
bool letterPressed;
//numerical value assoicated with a particular movement
int moveFlag;
//the last letter that the cursor selected on letterPressed
char curChar;
//default no-arg constructor
Cursor(void);
//function to draw the cursor on screen
void draw(uLCD_4DGL *u);
//function to cover up the previous position of the cursor
void coverUp(uLCD_4DGL *u);
};
class Player : public Entity {
public:
//Width of the p;ayer in pixels
int w;
//Height of the player in pixels
int h;
//(deprecated) color of the player in RGB
int color;
//default no-arg constructor
Player(void);
//flag to see if the user has moved using the joystick
bool hasMoved;
//flag to see if the user is jumping
bool isJumping;
//flag to see if the user is falling
bool isFalling;
//Used to keep track of how long until the player reaches the apex of the jump
int upCounter;
//Used to check how long until the player lands
int fallCounter;
//value representing some movement of direction
int moveFlag;
};
class Obstacle : public Entity {
public:
//(deprecated) color of the obstacle in RGB
int color;
//width of the obstacle in pixels
int w;
//height of the obstacle in pixels
int h;
//default constructor; color input variable is deprecated
Obstacle(int c);
};
class Counter : public Entity {
};
class Screen {
private:
public:
//(deprecated) sector address of the screen's background
int bgAddr;
//current position, on or off-screen, in pixels
int pos;
//
void drawScreen(void);
//increase the position values of each object and screen, so that they appear to be moving
void decScreen(void);
//default constructor
Screen(int bg, int initPos);
//main constructor, though the boolean flag serves no purpose and should be removed
Screen(int bg, bool randomSpots, int initPos);
//the 4 cactus obstacles associated with this screen
Obstacle *obs[4];
//Class destructor. Needed to delete both the screen and its associated obstacle objects
~Screen(void);
};
//(Deprecated) draw the rectangles of the obstacles while also checking if they are on-screen
void DrawIfOnScreen(Obstacle *o, uLCD_4DGL *u);
//draw the cactus image for each obstacle, also checking if any part of the obstacle is off-screen, and drawing the appropriate image if so
void DrawSliceBySlice(Obstacle *o, uLCD_4DGL *u);
//Draw a filled rectangle with the background color to hide the pixels of the previous position of an object
void DrawCoverUp(int row, int col, int w, int h, uLCD_4DGL *u);
//Check if any screens are now past the player, and rearrange the screens if so
void evalScreens(Screen **s);
//draw all objects associated with a screen
void drawScreens(uLCD_4DGL *u, Screen **s);
//draw the player sprite
void drawPlayer(uLCD_4DGL *u, Player *p);
//(deprecated) draw the cover up rectangle for x-axis movement of the player
void drawPlayerCoverUp(uLCD_4DGL *u, Player *p);
//Draw the cover up rectangle for x-axis movement of the player
void drawPlayerCoverUp(uLCD_4DGL *u, Player *p, int dir);
//draw the cover up rectangle for y-axis movement of the player
void drawPlayerAirborneCoverUp(uLCD_4DGL *u, Player *p);
//Cover up the small square that occurs if the user moves diagonally
void drawPlayerDiagCoverUp(uLCD_4DGL *u, Player *p);
//Draw the sun and any other background objects
void drawScenery(uLCD_4DGL *u);
//Collision Detection
//check a collision on the top-right corner of the player
bool detectTR(Obstacle *o,Player *p);
//check a collision on the bottom-left corner of the player
bool detectBL(Obstacle *o,Player *p);
//check a collision on the bottom-right corner of the player
bool detectBR(Obstacle *o,Player *p);
//check a collision on the top-left corner of the player
bool detectTL(Obstacle *o, Player *p);
//main function for collision detection; runs previous four functions and sets appropriate flags
void CollisionDetect(Screen **s, Player *p,bool *flag);
//draw the score counter on the right side of the screen
void drawScore(uLCD_4DGL *u, int score);
//Reset flags and other objects for a new game
void initialize(Screen **s, Player *p, int *score, bool* initFlash, bool *scDrawn, bool *collide, int *c, int *r, Cursor *curs);
Please log in to post comments.
