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

MbedNavSwitch
gnd-
p21R
p22D
p23L
p24C
p25U
nc+

LCD Screen

MbeduLCD cable
gndGND
VU+5V
p9TX
p10RX
p11RES

Image

/media/uploads/slandry8/20160314_163057.jpg

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.