ELEC2645 (2015/16)
/
Dodgemania
Wall dodging game utilising a joystick and Nokia 5110 LCD display
Diff: main.cpp
- Revision:
- 0:6b29f9c29a2a
- Child:
- 1:26ebbb94cf36
diff -r 000000000000 -r 6b29f9c29a2a main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Apr 22 19:38:51 2016 +0000 @@ -0,0 +1,619 @@ +/* Joystick +Max Hamilton + +My project will be designing a game in which the player must avoid a number +of obstacles coming from different locations for as long as possible + +Week 19 Code - Initial test of mbed screen and initilisations +Week 20 Code - Add code to move a player controlled ball around the screen +Week 21 Code - Add code to generate and move an obstacle down the screen + +Week 22 Code - Significant progress over easter holiday + - Diagonal directions added to joystick + - Collisions implemented. Game over triggers if character hits a wall + - Walls/obstacles developed greatly + - Obstacle extended into full wall. Wall structs created and walls travel from all sides of the screen + - Walls start of appearing from one side of the screen. As time goes on, more will appear from differnt sides, with multiple walls on screen at once + - Cooldown added to walls to ensure a new one will not instantly appear from a side that another wall has just travelled to. + - visual warning on screen when walls are about to appear from a new direction + - Game now keeps track of score and displays it at the end of the game + - Intro screen added + +*/ + +#include "mbed.h" +#include "N5110.h" +#include "tones.h" + +#define DIRECTION_TOLERANCE 0.05 // tolerance of joystick direction +#define PLAYERRADIUS 2 // size of player ball +#define GAMESPEED 20 // game timer speed +#define JOYSPEED 20 // rate at which the joystick is read +#define PI 3.14159265359 + +// VCC, SCE, RST, D/C, MOSI, SCLK, LED +N5110 lcd (PTD3 , PTA0 , PTC4 , PTD0 , PTD2 , PTD1 , PTC3); + +AnalogIn backlight(PTB2); // pot to control brightness +DigitalIn button(PTB3); // joystick button object +AnalogIn xPot(PTB11); // joystick x direction object +AnalogIn yPot(PTB10); // joystick y direction object +DigitalOut buzzer(PTA2); // buzzer object + +Ticker pollJoystick; // timer to regularly read the joystick +Ticker game_timer; // timer to regularly update the screen +Timer noteTimer; // timer for note tones + +Serial serial(USBTX,USBRX); // Serial for debug + +// create enumerated type (0,1,2,3 etc. for direction) +enum DirectionName { + UP, + DOWN, + LEFT, + RIGHT, + UPRIGHT, // Diagonally up + right + UPLEFT, // Diagonally up + left + DOWNRIGHT, // Diagonally down + right + DOWNLEFT, // Diagonally down + left + CENTRE, + UNKNOWN +}; + +typedef struct JoyStick Joystick; // struct for Joystick +typedef struct Wall Wall; // struct for Walls + +struct JoyStick { + float x; // current x value + float x0; // 'centred' x value + float y; // current y value + float y0; // 'centred' y value + int button; // button state (assume pull-down used, so 1 = pressed, 0 = unpressed) + DirectionName direction; // current direction +}; + +struct Wall { + int x; // x-coordinate of wall (realtive to centre of the gap) + int y; // y-coordinate of wall (relative to centre of the gap) + DirectionName direction; // Direction the wall travels in + int random; // randomly generated integer to determine when a wall begins to travel + int cooldown; // stops a wall respawning before a certain amount of time + bool moveFlag; // flag to determine if wall is on screen + bool genFlag; // flag to determine if wall has been generated +}; + +// struct variable for joystick +Joystick joystick; +// struct variable for moving walls +Wall leftwall; +Wall rightwall; +Wall downwall; +Wall upwall; + +void calibrateJoystick(); // read default positions of the joystick to calibrate later readings +void updateJoystick(); // reads direction the joystick has been moved +void initDisplay(); // initialises the LCD display +void game_timer_isr(); // sets flag for timer interrupt +void moveBall(); // reads joystick direction and moves position of the player +void moveWall(); // moves walls along the screen +void checkCollision(); // checks for any collisions (i.e. player has hit a wall or side of the screen) +void updateScreen(); // refreshes the screen, redraws player and walls +void warning(); // flashes screen when a new wall is ready to appear +void init_serial(); // sets baud rate for serial +void debug(); // prints for debug purposes + +float refresh_rate = GAMESPEED; // how often to update display (Hz) +float g_dt = 1.0F/refresh_rate; // global to store time step (F makes it a float, gets rid of compiler warning) +volatile int g_timer_flag = 0; // flag for timer interrupt +int printFlag = 0; // flag for printing +int game_over_flag = 0; // flag to signal game over +int game_start_flag = 0; // flag to start the game +int i = 42; // x-coordinate value of player +int j = 24; // y-coordinate value of player +int counter = 0; // number of times code has looped + + + +int main() +{ + init_serial(); + srand(time(NULL)); // generate seed for random number generation + calibrateJoystick(); + pollJoystick.attach(&updateJoystick,1.0/JOYSPEED); // read joystick (JOYSPEED) times per second + initDisplay(); + + + wait (1.0); + + game_timer.attach(&game_timer_isr,g_dt); + + while(1) { + + lcd.printString("Dodgemania",13,2); // Print game title on screen + wait (2.5); + for (int z=0; z<88; z++) { + lcd.drawCircle(z,20,4,1); + + lcd.clearPixel(z-3,16); + lcd.clearPixel(z-4,17); + lcd.clearPixel(z-4,18); + lcd.clearPixel(z-5,19); + lcd.clearPixel(z-5,20); + lcd.clearPixel(z-5,21); + lcd.clearPixel(z-4,22); + lcd.clearPixel(z-4,23); + lcd.clearPixel(z-3,24); + lcd.refresh(); + wait(0.01); + } + + lcd.clear(); + wait(0.5); + + lcd.inverseMode(); + wait(0.2); + lcd.normalMode(); + + lcd.printString("Press button",6,2); + lcd.printString("to start",18,3); + + while(game_start_flag == 0) { // replace with interrupt? + if(button) { + game_start_flag = 1; + lcd.clear(); + wait(0.5); + } + } + + lcd.inverseMode(); + wait(0.2); + lcd.normalMode(); + + // Draw game border + lcd.drawLine(2,2,81,2,1); + lcd.drawLine(2,2,2,45,1); + lcd.drawLine(81,2,81,45,1); + lcd.drawLine(2,45,81,45,1); + + lcd.drawLine(0,0,83,0,1); + lcd.drawLine(0,0,0,47,1); + lcd.drawLine(83,0,83,47,1); + lcd.drawLine(0,47,83,47,1); + + lcd.refresh(); + + // Countdown + wait(0.5); + lcd.printString("3",40,2); + wait(0.5); + lcd.drawRect(10,10,64,28,2); + lcd.refresh(); + wait(0.5); + lcd.printString("2",40,2); + wait(0.5); + lcd.drawRect(10,10,64,28,2); + lcd.refresh(); + wait(0.5); + lcd.printString("1",40,2); + wait(0.5); + lcd.drawRect(10,10,64,28,2); + lcd.refresh(); + wait(0.5); + lcd.drawRect(10,10,64,28,2); + lcd.refresh(); + lcd.printString("Go!",36,2); + wait(0.5); + lcd.drawRect(10,10,64,28,2); + lcd.refresh(); + + + leftwall.y = rand() % 27+8; + rightwall.y = rand() % 27+8; + downwall.x = rand() % 27+8; + upwall.x = rand() % 27+8; + rightwall.cooldown = 61; + + while (game_over_flag == 0) { + + if ( g_timer_flag ) { // ticker interrupt + g_timer_flag = 0; // clear flag + moveWall(); + moveBall(); + checkCollision(); + updateScreen(); + warning(); + debug(); + + counter++; // increment counter each cycle (approx. 20 points a second) + + // wall cooldowns increased. N.B these are set to 0 when a wall finishes moving + leftwall.cooldown++; + rightwall.cooldown++; + downwall.cooldown++; + upwall.cooldown++; + } + sleep(); + } + + lcd.inverseMode(); + wait(0.1); + lcd.normalMode(); + wait(2.0); + lcd.clear(); + wait(0.2); + + lcd.drawRect(11,5,62,11,0); + lcd.printString("Game Over!",14,1); + lcd.refresh(); + + wait(1.0); + lcd.drawRect(11,21,62,21,0); + lcd.printString("Score:",14,3); + char buffer[14]; + int length = sprintf(buffer,"%d",counter); + if (counter <= 999999999) { // 999999999 is highest number that fits in score display box + lcd.printString(buffer,14,4); + } else { + lcd.printString ("Too High!",14,4); // if score is too large to fit in box (unlikely!) + } + lcd.refresh(); + + wait(5.0); + lcd.inverseMode(); + wait(0.2); + lcd.normalMode(); + lcd.clear(); + + return 0; + } + +} + +void moveBall() // reads joystick direction and moves position of the player +{ + if (joystick.direction == UP) { + j-=1; + } else if (joystick.direction == DOWN) { + j+=1; + } else if (joystick.direction == LEFT) { + i-=1; + } else if (joystick.direction == RIGHT) { + i+=1; + } else if (joystick.direction == UPRIGHT) { + j-=1; + i+=1; + } else if (joystick.direction == UPLEFT) { + j-=1; + i-=1; + } else if (joystick.direction == DOWNRIGHT) { + j+=1; + i+=1; + } else if (joystick.direction == DOWNLEFT) { + j+=1; + i-=1; + } +} + +void moveWall() // moves walls along the screen +{ + leftwall.random = rand()%20; + rightwall.random = rand()%20; + downwall.random = rand()%50; + upwall.random = rand()%50; + + // LEFT WALL + if (leftwall.moveFlag == 1) { // if wall is moving + leftwall.x-=1; // move wall left + if (leftwall.x<3) { // if wall hits a border + leftwall.moveFlag = 0; // stop wall moving + leftwall.cooldown = 0; // reset the cooldown for the wall + } + } else { // if wall has stopped moving + if (leftwall.genFlag == 0) { // if a new wall HASN'T been generated + leftwall.y = rand() % 27+8; // make new random y-coordinate + leftwall.x = 82; // reset x-coordinate to rightmost position + leftwall.genFlag = 1; // wall has been generated + } else { // if a new wall HAS been generated + if (leftwall.cooldown > 80) { // if a new wall hasnt started moving in 4 seconds, force it to move + leftwall.moveFlag = 1; + leftwall.genFlag = 0; + } else if ((leftwall.random == 1)&&(rightwall.cooldown > 60)) { // if wall starts moving again + leftwall.moveFlag = 1; // start wall moving + leftwall.genFlag = 0; // clear 'wall generated' flag + } else { // else if wall has not started moving again + leftwall.moveFlag = 0; // wall is stopped + } + } + } + +// RIGHT WALL + if (counter > 200) { + if (rightwall.moveFlag == 1) { + rightwall.x+=1; + if (rightwall.x>80) { + rightwall.moveFlag = 0; + rightwall.cooldown = 0; + } + } else { + if ((rightwall.genFlag == 0)) { + rightwall.y = rand() % 27+8; + rightwall.x = 1; + rightwall.genFlag = 1; + } else { + if (rightwall.cooldown > 80) { + rightwall.moveFlag = 1; + rightwall.genFlag = 0; + } else if ((rightwall.random == 1) && (leftwall.cooldown > 60)) { + rightwall.moveFlag = 1; + rightwall.genFlag = 0; + } else { + rightwall.moveFlag = 0; + } + } + } + } + +// DOWN WALL + if (counter > 600) { + if (downwall.moveFlag == 1) { + if (upwall.cooldown > 60) { + if (counter % 2 == 1) { // horizontal walls move half the speed of vertical walls + downwall.y+=1; + } + } + if (downwall.y>44) { + downwall.moveFlag = 0; + } + } else { + if (downwall.genFlag == 0) { + downwall.x = rand() % 63+8; + downwall.y = 1; + downwall.genFlag = 1; + } else { + if (downwall.cooldown > 80) { + downwall.moveFlag = 1; + downwall.genFlag = 0; + } else if ((downwall.random == 1)&&(upwall.cooldown > 60)) { + downwall.moveFlag = 1; + downwall.genFlag = 0; + } else { + downwall.moveFlag = 0; + } + } + } + } + +// UP WALL + if (counter > 1500) { + if (upwall.moveFlag == 1) { + if (downwall.cooldown > 60) { + if (counter % 2 == 1) { // horizontal walls move half the speed of vertical walls + upwall.y-=1; + } + } + if (upwall.y<3) { + upwall.moveFlag = 0; + } + } else { + if (upwall.genFlag == 0) { + upwall.x = rand() % 63+8; + upwall.y = 46; + upwall.genFlag = 1; + } else { + if (upwall.cooldown > 80) { + upwall.moveFlag = 1; + upwall.genFlag = 0; + } else if ((upwall.random == 1)&&(downwall.cooldown > 60)) { + upwall.moveFlag = 1; + upwall.genFlag = 0; + } else { + upwall.moveFlag = 0; + } + } + } + } +} + +void checkCollision() // checks for any collisions (i.e. player has hit a wall or side of the screen) +{ + // if floor + if ( j >= 47 - (PLAYERRADIUS+3)) { + j = 47 - (PLAYERRADIUS+3); + } + + // if roof + if ( j <= (PLAYERRADIUS+3)) { + j = (PLAYERRADIUS+3); + } + + // if right wall + if ( i >= 83 - (PLAYERRADIUS+3)) { + i = 83 - (PLAYERRADIUS+3); + } + + // if left wall + if ( i <= (PLAYERRADIUS+3)) { + i = (PLAYERRADIUS+3); + } + + // LEFT WALL + if ((((i - PLAYERRADIUS) <= leftwall.x) && (leftwall.x <= (i + PLAYERRADIUS))) && (j > (leftwall.y+3) || j < (leftwall.y-3))) { + game_over_flag = 1; + } + + // RIGHT WALL + if ((((i - PLAYERRADIUS) <= rightwall.x) && (rightwall.x <= (i + PLAYERRADIUS))) && (j > (rightwall.y+3) || j < (rightwall.y-3))) { + game_over_flag = 1; + } + + // DOWN WALL + if ((((j - PLAYERRADIUS) <= downwall.y) && (downwall.y <= (j - PLAYERRADIUS))) && (i > (downwall.x+9) || i < (downwall.x-9))) { // if player x is 4 or more than wall x, it has missed the gap + game_over_flag = 1; + } + + // UP WALL + if (((j + PLAYERRADIUS) == upwall.y || (j - PLAYERRADIUS) == upwall.y) && (i > (upwall.x+9) || i < (upwall.x-9))) { // if player x is 4 or more than wall x, it has missed the gap + game_over_flag = 1; + } +} + +void updateScreen() // refreshes the screen, redraws player and walls +{ + lcd.clear(); + lcd.drawCircle(i,j,PLAYERRADIUS,1); + lcd.refresh(); // update display + + // draw Border + lcd.drawLine(2,2,81,2,1); + lcd.drawLine(2,2,2,45,1); + lcd.drawLine(81,2,81,45,1); + lcd.drawLine(2,45,81,45,1); + + lcd.drawLine(0,0,83,0,1); + lcd.drawLine(0,0,0,47,1); + lcd.drawLine(83,0,83,47,1); + lcd.drawLine(0,47,83,47,1); + + // draw walls + // LEFT WALL + lcd.drawLine(leftwall.x,leftwall.y+5,leftwall.x,44,1); + lcd.drawLine(leftwall.x,leftwall.y-5,leftwall.x,3,1); + lcd.drawLine(leftwall.x+1,leftwall.y+5,leftwall.x+1,44,1); + lcd.drawLine(leftwall.x+1,leftwall.y-5,leftwall.x+1,3,1); + + // RIGHT WALL + if (counter > 200) { + lcd.drawLine(rightwall.x,rightwall.y+5,rightwall.x,44,1); + lcd.drawLine(rightwall.x,rightwall.y-5,rightwall.x,3,1); + lcd.drawLine(rightwall.x-1,rightwall.y+5,rightwall.x-1,44,1); + lcd.drawLine(rightwall.x-1,rightwall.y-5,rightwall.x-1,3,1); + } + + // DOWN WALL + if (counter > 600) { + lcd.drawLine(downwall.x+11,downwall.y,80,downwall.y,1); + lcd.drawLine(downwall.x-11,downwall.y,3,downwall.y,1); + lcd.drawLine(downwall.x+11,downwall.y-1,80,downwall.y-1,1); + lcd.drawLine(downwall.x-11,downwall.y-1,3,downwall.y-1,1); + } + + // UP WALL + if (counter > 1500) { + lcd.drawLine(upwall.x+11,upwall.y,80,upwall.y,1); + lcd.drawLine(upwall.x-11,upwall.y,3,upwall.y,1); + lcd.drawLine(upwall.x+11,upwall.y+1,80,upwall.y+1,1); + lcd.drawLine(upwall.x-11,upwall.y+1,3,upwall.y+1,1); + } + + lcd.refresh(); +} + +void warning () +{ + if (counter == 170) { + lcd.inverseMode(); + } else if (counter == 570) { + lcd.inverseMode(); + } else if (counter == 1470) { + lcd.inverseMode(); + } else { + lcd.normalMode(); + } +} + +void game_timer_isr() // sets flag for timer interrupt +{ + g_timer_flag = 1; +} + +void initDisplay() // initialises the LCD display +{ + lcd.init(); + lcd.normalMode(); + lcd.setBrightness(1.0F-backlight); // brightness pot on PCB is soldered in the wrong direction, (1.0F-backlight) inverts the reading +} + +void calibrateJoystick() // read default positions of the joystick to calibrate later readings +{ + button.mode(PullDown); + // must not move during calibration + joystick.x0 = xPot; // initial positions in the range 0.0 to 1.0 (0.5 if centred exactly) + joystick.y0 = yPot; +} +void updateJoystick() // reads direction the joystick has been moved +{ + // read current joystick values relative to calibrated values (in range -0.5 to 0.5, 0.0 is centred) + joystick.x = xPot - joystick.x0; + joystick.y = yPot - joystick.y0; + // read button state + joystick.button = button; + + // calculate direction depending on x,y values + // tolerance allows a little lee-way in case joystick not exactly in the stated direction + if ( fabs(joystick.y) < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { + joystick.direction = CENTRE; + } else if ( joystick.y > DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { + joystick.direction = UP; + } else if ( joystick.y < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { + joystick.direction = DOWN; + } else if ( joystick.x > DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) { + joystick.direction = LEFT; + } else if ( joystick.x < DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) { + joystick.direction = RIGHT; + } else if ( joystick.y > DIRECTION_TOLERANCE && joystick.x > DIRECTION_TOLERANCE) { + joystick.direction = UPLEFT; + } else if ( joystick.y > DIRECTION_TOLERANCE && joystick.x < DIRECTION_TOLERANCE) { + joystick.direction = UPRIGHT; + } else if ( joystick.x > DIRECTION_TOLERANCE && joystick.y < DIRECTION_TOLERANCE) { + joystick.direction = DOWNLEFT; + } else if ( joystick.x < DIRECTION_TOLERANCE && joystick.y < DIRECTION_TOLERANCE) { + joystick.direction = DOWNRIGHT; + } + + else { + joystick.direction = UNKNOWN; + } + + + printFlag = 1; // set flag for printing +} + +void debug() // prints for debug purposes +{ + if (printFlag) { // if flag set, clear flag and print joystick values to serial port + printFlag = 0; + /* + if (joystick.direction == UP) + serial.printf(" UP\n"); + if (joystick.direction == DOWN) + serial.printf(" DOWN\n"); + if (joystick.direction == LEFT) + serial.printf(" LEFT\n"); + if (joystick.direction == RIGHT) + serial.printf(" RIGHT\n"); + if (joystick.direction == UPRIGHT) + serial.printf(" UPRIGHT\n"); // Diagonally up + right + if (joystick.direction == UPLEFT) + serial.printf(" UPLEFT\n"); // Diagonally up + left + if (joystick.direction == DOWNRIGHT) + serial.printf(" DOWNRIGHT\n"); // Diagonally down + right + if (joystick.direction == DOWNLEFT) + serial.printf(" DOWNLEFT\n"); // Diagonally down + left + if (joystick.direction == CENTRE) + serial.printf(" CENTRE\n"); + if (joystick.direction == UNKNOWN) + serial.printf(" Unsupported direction\n"); + */ + serial.printf("Left-wall Cooldown = %d \n",leftwall.cooldown); + serial.printf("Right-wall Cooldown = %d \n",rightwall.cooldown); + serial.printf("counter = %d \n",counter); + + } +} + +void init_serial() // sets baud rate for serial +{ + // set to highest baud - ensure terminal software matches + serial.baud(115200); +} +