ELEC2645 (2015/16)
/
Dodgemania
Wall dodging game utilising a joystick and Nokia 5110 LCD display
main.cpp
- Committer:
- el14moh
- Date:
- 2016-04-22
- Revision:
- 1:26ebbb94cf36
- Parent:
- 0:6b29f9c29a2a
- Child:
- 2:602e9bb053a0
File content as of revision 1:26ebbb94cf36:
/* 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 volatile bool moveFlag; // flag to determine if wall is on screen volatile 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 volatile 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); }