ECE 4180 @ Georgia Tech

Dependencies:   mbed mbed-rtos 4DGL-uLCD-SE MMA8452

main.cpp

Committer:
snorod
Date:
2020-04-30
Revision:
10:6f15dcee0a72
Parent:
8:31e63caf37e2

File content as of revision 10:6f15dcee0a72:

#include "mbed.h"
#include "uLCD_4DGL.h"
#include "rtos.h"
#include "MMA8452.h"

#define LEFT_X 45
#define CENTER_X 65
#define RIGHT_X 85

#define JUMP_Y 96
#define CENTER_Y 108
#define DUCK_Y 120

#define RADIUS 4

#define SPEED 5
#define VOLUME 0.1

Serial pc(USBTX,USBRX); // included for debugging accelerometer
MMA8452 acc(p28, p27, 100000);
uLCD_4DGL uLCD(p9,p10,p11); // serial tx, serial rx, reset pin;
PwmOut jack(p23);
DigitalIn jump_button(p22);
DigitalIn duck_button(p21);

// mutex for writing to the uLCD
Mutex LCD;

volatile int score;
volatile int high_score;
volatile int steps;
volatile int strikes;
double acc_x;
volatile int player_x;
volatile int player_y;

volatile int obstacle_x;
volatile int obstacle_y;
volatile int dodge_x;

int ob_x_roots[] = {61, 65, 69};
const char* strikes_arr[4] = { "   ", "  X", " XX", "XXX" };
enum ob_type {dodge, duck, jump};
ob_type ob_type_arr[] = {dodge, duck, jump};

volatile ob_type obstacle_type;

volatile bool play_leftright;
volatile bool play_jump;
volatile bool play_duck;
volatile bool play_strikes;
volatile bool play_dead;

void read_sound(void const *argument) {
    while(1) {

        // different sounds in order of priority to audio output
        if (play_dead) {
            jack.period(1.0/440.0);
            jack = VOLUME/2.0;
            Thread::wait(750);
            jack = 0.0;
            Thread::wait(100);
            jack = VOLUME/2.0;
            Thread::wait(750);
            jack = 0.0;
            Thread::wait(100);
            jack = VOLUME/2.0;
            Thread::wait(750);
            jack = 0.0;
            play_dead = false;
            play_strikes = false;
            play_duck = false;
            play_jump = false;
            play_leftright = false;
        } else if (play_strikes) {
            jack.period(1.0/440.0);
            jack = VOLUME/2.0;
            Thread::wait(750);
            jack = 0.0;
            play_strikes = false;
            play_duck = false;
            play_jump = false;
            play_leftright = false;
        } else if (play_duck) {
            jack.period(1.0/130.8);
            jack = VOLUME/2.0;
            Thread::wait(1000);
            jack = 0.0;
            play_duck = false;
            play_jump = false;
            play_leftright = false;
        } else if (play_jump) {
            jack.period(1.0/261.6);
            jack = VOLUME/2.0;
            Thread::wait(150);
            jack.period(1.0/523.3);
            Thread::wait(300);
            jack = 0.0;
            play_jump = false;
            play_leftright = false;
        } else if (play_leftright) {
            jack.period(1.0/261.6);
            jack = VOLUME/2.0;
            Thread::wait(250);
            jack = 0.0;
            play_leftright = false;
        }
    }
}

void read_acc(void const *argument){
    while(1) {
        acc.readXGravity(&acc_x);
        if (acc_x > 0.5) {
            if (player_x == CENTER_X) {
                player_x = LEFT_X;
            } else if (player_x == RIGHT_X) {
                player_x = CENTER_X;
            }
            play_leftright = true;
            Thread::wait(500);
        } else if (acc_x < -0.5) {
            if (player_x == CENTER_X) {
                player_x = RIGHT_X;
            } else if (player_x == LEFT_X) {
                player_x = CENTER_X;
            }
            play_leftright = true;
            Thread::wait(500);
        }
        Thread::wait(250);
    }
}

void read_score(void const *argument) {
    while(1) {
        score = score + 10;
        LCD.lock();
        uLCD.locate(11, 0);
        uLCD.printf("%7D", score); // maximum 7-digit score, cap at 9,999,900
        LCD.unlock();
        Thread::wait(1000);
    }
}

void read_high_score(void const *argument) {
    while(1) {
        LCD.lock();
        uLCD.locate(0, 0);
        uLCD.printf("HIGH:\n%D", high_score);
        LCD.unlock();
        Thread::wait(1500);
    }
}

void read_strikes(void const *argument) {
    while(1) {
        LCD.lock();
        uLCD.locate(15, 1);
        uLCD.printf(strikes_arr[strikes % 4]);
        LCD.unlock();
        Thread::wait(1000);
    }
}

// check for button press
void read_duck(void const *argument) {
    while(1) {
        if (!duck_button) {
            player_y = DUCK_Y;
            play_duck = true;
            Thread::wait(1000);
            player_y = CENTER_Y;
        }
    }
}

// check for button press
void read_jump(void const *argument) {
    while(1) {
        if (!jump_button && player_y != DUCK_Y) {
            player_y = JUMP_Y;
            play_jump = true;
            Thread::wait(1000);
            if (player_y == JUMP_Y) {
                player_y = CENTER_Y;
            }
        }
    }
}

// check if it's game over
void read_alive(void const *argument) {
    while(1) {
        if (strikes >= 3 || score >= 9999900) {
            if (score > high_score) {
                high_score = score;
            }
            score = 0;
            strikes = 0;
            player_x = CENTER_X;
            player_y = CENTER_Y;
        }
    }
}

void read_position(void const *argument) {
    while(1) {
        LCD.lock();

        // cover up positions on screen where there's no player or obstacle
        if (player_x != LEFT_X) {
            if (obstacle_y != JUMP_Y) {
                uLCD.filled_circle(LEFT_X, JUMP_Y, RADIUS, 0x003057);
            }
            if (obstacle_y != CENTER_Y) {
                uLCD.filled_circle(LEFT_X, CENTER_Y, RADIUS, 0x003057);
            }
            if (obstacle_y != DUCK_Y) {
                uLCD.filled_circle(LEFT_X, DUCK_Y, RADIUS, 0x003057);
            }
        }
        if (player_x != CENTER_X) {
            if (obstacle_y != JUMP_Y) {
                uLCD.filled_circle(CENTER_X, JUMP_Y, RADIUS, 0x003057);
            }
            if (obstacle_y != CENTER_Y) {
                uLCD.filled_circle(CENTER_X, CENTER_Y, RADIUS, 0x003057);
            }
            if (obstacle_y != DUCK_Y) {
                uLCD.filled_circle(CENTER_X, DUCK_Y, RADIUS, 0x003057);
            }
        }
        if (player_x != RIGHT_X) {
            if (obstacle_y != JUMP_Y) {
                uLCD.filled_circle(RIGHT_X, JUMP_Y, RADIUS, 0x003057);
            }
            if (obstacle_y != CENTER_Y) {
                uLCD.filled_circle(RIGHT_X, CENTER_Y, RADIUS, 0x003057);
            }
            if (obstacle_y != DUCK_Y) {
                uLCD.filled_circle(RIGHT_X, DUCK_Y, RADIUS, 0x003057);
            }
        }
        if (player_y != JUMP_Y && obstacle_y != JUMP_Y) {
            if (player_x == RIGHT_X) {
                uLCD.filled_circle(player_x, JUMP_Y, RADIUS, 0x003057);
            } else if (player_x == LEFT_X) {
                uLCD.filled_circle(player_x, JUMP_Y, RADIUS, 0x003057);
            } else if (player_x == CENTER_X) {
                uLCD.filled_circle(player_x, JUMP_Y, RADIUS, 0x003057);
            }
        }
        if (player_y != CENTER_Y && obstacle_y != CENTER_Y) {
            uLCD.filled_circle(player_x, CENTER_Y, RADIUS, 0x003057);
        }
        if (player_y != DUCK_Y && obstacle_y != DUCK_Y) {
            if (player_x == RIGHT_X) {
                uLCD.filled_circle(player_x, DUCK_Y, RADIUS, 0x003057);
            } else if (player_x == LEFT_X) {
                uLCD.filled_circle(player_x, DUCK_Y, RADIUS, 0x003057);
            } else if (player_x == CENTER_X) {
                uLCD.filled_circle(player_x, DUCK_Y, RADIUS, 0x003057);
            }
        }
        uLCD.filled_circle(player_x, player_y, RADIUS, 0xB3A369);
        LCD.unlock();
    }
}

void read_obstacle(void const *argument) {
    while(1) {
        LCD.lock();
        uLCD.filled_rectangle(obstacle_x - 10 - (steps * 2), obstacle_y + 7, obstacle_x + 10 + (steps * 2), obstacle_y - 7, 0x003057);
        steps++;
        obstacle_y = obstacle_y + 12;

        // move obstacle along slope down the track
        if (obstacle_type == dodge) {
            if (dodge_x >= ob_x_roots[2]) {
                dodge_x = dodge_x + 2;
            } else if (dodge_x <= ob_x_roots[0]) {
                dodge_x = dodge_x - 2;
            }
        }

        // reset obstacle if it has exited the screen
        if (obstacle_y >= 130) {
            obstacle_type = ob_type_arr[rand() % 3];
            obstacle_x = ob_x_roots[1];
            if (obstacle_type == dodge) {
                dodge_x = ob_x_roots[rand() % 3];
            }
            obstacle_y = 12;
            steps = 0;
        }

        // draw dodge obstacle
        if (obstacle_type != dodge) {
            uLCD.filled_rectangle(obstacle_x - 10 - (steps * 2), obstacle_y + 7, obstacle_x + 10 + (steps * 2), obstacle_y - 7, 0xEAAA00);
        } else {
            if (dodge_x >= ob_x_roots[2]) {
                // draw rectangle on left side, dodge on the right
                uLCD.filled_rectangle(obstacle_x - 10 - (steps * 2), obstacle_y + 7, dodge_x - 10, obstacle_y - 7, 0xEAAA00);
            } else if (dodge_x <= ob_x_roots[0]) {
                // draw rectangle on right side, dodge on the left
                uLCD.filled_rectangle(dodge_x + 10, obstacle_y + 7, obstacle_x + 10 + (steps * 2), obstacle_y - 7, 0xEAAA00);
            } else {
                // draw two rectangles on either side, dodge in the middle
                uLCD.filled_rectangle(ob_x_roots[0] - 5 - (steps * 2), obstacle_y + 7, ob_x_roots[0] - steps, obstacle_y - 7, 0xEAAA00);
                uLCD.filled_rectangle(ob_x_roots[2] + steps, obstacle_y + 7, ob_x_roots[2] + 5 + (steps * 2), obstacle_y - 7, 0xEAAA00);
            }
        }

        // draw duck/jump obstacle
        if (obstacle_type == duck) {
            uLCD.filled_rectangle(obstacle_x - 8 - (steps * 2), obstacle_y + 7, obstacle_x + 8 + (steps * 2), obstacle_y - 5, 0x003057);
        } else if (obstacle_type == jump) {
            uLCD.filled_rectangle(obstacle_x - 8 - (steps * 2), obstacle_y + 5, obstacle_x + 8 + (steps * 2), obstacle_y - 7, 0x003057);
        }

        // check for collisions, increment strikes if so
        if (obstacle_y == CENTER_Y) {
            if ((obstacle_type == duck && player_y != DUCK_Y) || (obstacle_type == jump && player_y != JUMP_Y) || (obstacle_type == dodge && dodge_x != player_x)) {
                strikes++;
                if (strikes >= 3) {
                    play_dead = true;
                } else {
                    play_strikes = true;
                }
            }
        }
        LCD.unlock();
        Thread::wait(SPEED);
    }
}

int main()
{
    uLCD.background_color(0x003057);
    uLCD.cls();
    uLCD.text_width(4);
    uLCD.text_height(4);
    uLCD.textbackground_color(0x003057);
    uLCD.color(0xB3A369);
    uLCD.printf("\nBuzz Run");

    wait(2);
    uLCD.cls();
    uLCD.baudrate(3000000); // high baud rate for fast refresh

    // Left-side guardrails
    uLCD.line(23, 130, 43, 10, 0xFFFFFF);
    uLCD.line(24, 130, 44, 10, 0xB3A369);
    uLCD.line(25, 130, 45, 10, 0xB3A369);
    uLCD.line(26, 130, 46, 10, 0xB3A369);
    uLCD.line(27, 130, 47, 10, 0xFFFFFF);

    // Right-side guardrails
    uLCD.line(103, 130, 83, 10, 0xFFFFFF);
    uLCD.line(104, 130, 84, 10, 0xB3A369);
    uLCD.line(105, 130, 85, 10, 0xB3A369);
    uLCD.line(106, 130, 86, 10, 0xB3A369);
    uLCD.line(107, 130, 87, 10, 0xFFFFFF);

    score = 0;
    high_score = 0;
    strikes = 0;
    steps = 0;

    // make sure sounds are off
    play_leftright = false;
    play_jump = false;
    play_duck = false;
    play_strikes = false;
    play_dead = false;

    // initialize player position
    player_x = CENTER_X;
    player_y = CENTER_Y;

    // initialize obstacle coordinates
    obstacle_x = ob_x_roots[1];
    dodge_x = ob_x_roots[rand() % 3];
    obstacle_y = 12;
    obstacle_type = ob_type_arr[rand() % 3];

    // pull up push_buttons
    jump_button.mode(PullUp);
    duck_button.mode(PullUp);

    // start threads
    Thread acc_thread(read_acc);
    Thread score_thread(read_score);
    Thread duck_thread(read_duck);
    Thread jump_thread(read_jump);
    Thread alive_thread(read_alive);
    Thread position_thread(read_position);
    Thread obstacle_thread(read_obstacle);
    Thread strikes_thread(read_strikes);
    Thread sound_thread(read_sound);
    Thread high_score_thread(read_high_score);

    while(1) {
        // loop forever
    }

}