“Race Collision” is a one player game in which a truck has to avoid “particles” that appear on the road. By the use of the joystick, the player can guide themselves through the menu system to start the game. The truck is the main element of the game and it can be moved from side to side with the joystick. The road curves randomly from time to time and the player has to be careful to keep the truck within the road boundaries. Particles appear on the screen at random positions and 4 collisions lead to the end of the game.
Dependencies: ELEC2645_JoystickLCD_LPC1768_2021
main.cpp@9:6f060f495536, 2021-05-06 (annotated)
- Committer:
- alex_20
- Date:
- Thu May 06 12:04:45 2021 +0000
- Revision:
- 9:6f060f495536
- Parent:
- 8:1fc5e14b0db6
Race Collision for ELEC2645
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
eencae | 0:be41a15e7a86 | 1 | /* mbed Microcontroller Library |
eencae | 0:be41a15e7a86 | 2 | * Copyright (c) 2019 ARM Limited |
eencae | 0:be41a15e7a86 | 3 | * SPDX-License-Identifier: Apache-2.0 |
eencae | 0:be41a15e7a86 | 4 | */ |
eencae | 0:be41a15e7a86 | 5 | |
eencae | 0:be41a15e7a86 | 6 | #include "mbed.h" |
alex_20 | 9:6f060f495536 | 7 | #include "ShiftReg.h" |
eencae | 0:be41a15e7a86 | 8 | #include "Joystick.h" |
eencae | 0:be41a15e7a86 | 9 | #include "N5110.h" |
alex_20 | 2:18fd28044860 | 10 | #include "Road.h" |
alex_20 | 6:40ef2030334c | 11 | #include "Car.h" |
alex_20 | 4:def20a1665d1 | 12 | #include "Utils.h" |
alex_20 | 4:def20a1665d1 | 13 | #include "Vector.h" |
alex_20 | 8:1fc5e14b0db6 | 14 | #include "Ball.h" |
alex_20 | 9:6f060f495536 | 15 | #include "Vector.h" |
alex_20 | 9:6f060f495536 | 16 | #include "GameEngine.h" |
alex_20 | 9:6f060f495536 | 17 | #include <stdlib.h> |
alex_20 | 9:6f060f495536 | 18 | #include <time.h> |
eencae | 0:be41a15e7a86 | 19 | |
alex_20 | 1:2ae7a8b01771 | 20 | // objects |
alex_20 | 1:2ae7a8b01771 | 21 | // BusOut leds(LED4,LED3,LED2,LED1); |
eencae | 0:be41a15e7a86 | 22 | |
alex_20 | 6:40ef2030334c | 23 | N5110 lcd(p14,p8,p9,p10,p11,p13,p21); |
alex_20 | 9:6f060f495536 | 24 | InterruptIn button_A(p29); |
alex_20 | 9:6f060f495536 | 25 | DigitalIn button_B(p28); |
alex_20 | 6:40ef2030334c | 26 | DigitalIn button_C(p27); |
alex_20 | 9:6f060f495536 | 27 | DigitalIn button_D(p26); |
alex_20 | 9:6f060f495536 | 28 | ShiftReg seven_seg; |
eencae | 0:be41a15e7a86 | 29 | |
alex_20 | 6:40ef2030334c | 30 | // y x |
alex_20 | 6:40ef2030334c | 31 | Joystick joystick(p20,p19); |
alex_20 | 1:2ae7a8b01771 | 32 | |
alex_20 | 2:18fd28044860 | 33 | // B G R |
alex_20 | 1:2ae7a8b01771 | 34 | //BusOut leds(p22,p23,p24); |
alex_20 | 1:2ae7a8b01771 | 35 | // LSB MSB |
alex_20 | 1:2ae7a8b01771 | 36 | |
alex_20 | 2:18fd28044860 | 37 | Road road; |
alex_20 | 4:def20a1665d1 | 38 | Utils utils; |
alex_20 | 6:40ef2030334c | 39 | Car car; |
alex_20 | 8:1fc5e14b0db6 | 40 | Ball ball; |
alex_20 | 9:6f060f495536 | 41 | GameEngine game; |
alex_20 | 9:6f060f495536 | 42 | Ticker ticker; |
alex_20 | 9:6f060f495536 | 43 | |
alex_20 | 9:6f060f495536 | 44 | // flag - must be volatile as changes within ISR |
alex_20 | 9:6f060f495536 | 45 | // g_ prefix makes it easier to distinguish it as global |
alex_20 | 9:6f060f495536 | 46 | volatile int g_timer_flag = 0; |
alex_20 | 9:6f060f495536 | 47 | volatile int g_buttonA_flag = 0; |
alex_20 | 2:18fd28044860 | 48 | |
alex_20 | 4:def20a1665d1 | 49 | #define speed 0.01 |
alex_20 | 8:1fc5e14b0db6 | 50 | #define road_speed 0.016 |
alex_20 | 6:40ef2030334c | 51 | #define car_speed 0.2 |
alex_20 | 6:40ef2030334c | 52 | |
alex_20 | 1:2ae7a8b01771 | 53 | // functions |
alex_20 | 1:2ae7a8b01771 | 54 | void init_buttons(); |
alex_20 | 9:6f060f495536 | 55 | void init(); |
alex_20 | 9:6f060f495536 | 56 | void render(); |
alex_20 | 9:6f060f495536 | 57 | void menu(); |
alex_20 | 9:6f060f495536 | 58 | void instructions(); |
alex_20 | 9:6f060f495536 | 59 | void pause(); |
alex_20 | 9:6f060f495536 | 60 | void game_over(); |
alex_20 | 9:6f060f495536 | 61 | void draw_arrow(int x, int y); |
alex_20 | 9:6f060f495536 | 62 | void timer_isr(); |
alex_20 | 9:6f060f495536 | 63 | void buttonA_isr(); |
alex_20 | 9:6f060f495536 | 64 | void display_lives(int lives); |
eencae | 0:be41a15e7a86 | 65 | |
eencae | 0:be41a15e7a86 | 66 | int main() |
eencae | 0:be41a15e7a86 | 67 | { |
alex_20 | 9:6f060f495536 | 68 | button_A.rise(&buttonA_isr); |
alex_20 | 9:6f060f495536 | 69 | init_buttons(); |
alex_20 | 9:6f060f495536 | 70 | int state = 0; // set inital state |
alex_20 | 9:6f060f495536 | 71 | init(); |
alex_20 | 9:6f060f495536 | 72 | // welcome(); |
alex_20 | 9:6f060f495536 | 73 | int up_down = 17; |
alex_20 | 9:6f060f495536 | 74 | float number_1 = 0; |
alex_20 | 9:6f060f495536 | 75 | float number_2 = 0; |
alex_20 | 9:6f060f495536 | 76 | int lives = 4; |
alex_20 | 9:6f060f495536 | 77 | int old_collision = 0; |
alex_20 | 9:6f060f495536 | 78 | display_lives(0); |
alex_20 | 9:6f060f495536 | 79 | |
alex_20 | 9:6f060f495536 | 80 | // set-up the ticker so that the ISR it is called every 5 seconds |
alex_20 | 9:6f060f495536 | 81 | ticker.attach(&timer_isr,5); |
alex_20 | 6:40ef2030334c | 82 | |
alex_20 | 1:2ae7a8b01771 | 83 | while(1) { |
alex_20 | 1:2ae7a8b01771 | 84 | lcd.clear(); |
alex_20 | 9:6f060f495536 | 85 | Vector2D coord = joystick.get_mapped_coord(); |
alex_20 | 9:6f060f495536 | 86 | switch(state) { |
alex_20 | 4:def20a1665d1 | 87 | |
alex_20 | 9:6f060f495536 | 88 | case 0: // Main Menu |
alex_20 | 9:6f060f495536 | 89 | { |
alex_20 | 9:6f060f495536 | 90 | menu(); |
alex_20 | 9:6f060f495536 | 91 | lives = 4; |
alex_20 | 9:6f060f495536 | 92 | display_lives(0); |
alex_20 | 9:6f060f495536 | 93 | if(coord.y >= 0.5) { |
alex_20 | 9:6f060f495536 | 94 | up_down = 17; |
alex_20 | 9:6f060f495536 | 95 | } |
alex_20 | 9:6f060f495536 | 96 | |
alex_20 | 9:6f060f495536 | 97 | if(coord.y <= -0.5) { |
alex_20 | 9:6f060f495536 | 98 | up_down = 25; |
alex_20 | 9:6f060f495536 | 99 | } |
alex_20 | 9:6f060f495536 | 100 | |
alex_20 | 9:6f060f495536 | 101 | draw_arrow(2,up_down); |
alex_20 | 9:6f060f495536 | 102 | |
alex_20 | 9:6f060f495536 | 103 | if(g_buttonA_flag) { |
alex_20 | 9:6f060f495536 | 104 | if(up_down == 17){ |
alex_20 | 9:6f060f495536 | 105 | state = 1; |
alex_20 | 9:6f060f495536 | 106 | g_buttonA_flag = 0; |
alex_20 | 9:6f060f495536 | 107 | } |
alex_20 | 9:6f060f495536 | 108 | if(up_down == 25) { |
alex_20 | 9:6f060f495536 | 109 | state = 2; |
alex_20 | 9:6f060f495536 | 110 | g_buttonA_flag = 0; |
alex_20 | 9:6f060f495536 | 111 | } |
alex_20 | 9:6f060f495536 | 112 | } |
alex_20 | 9:6f060f495536 | 113 | break; |
alex_20 | 6:40ef2030334c | 114 | } |
alex_20 | 9:6f060f495536 | 115 | |
alex_20 | 9:6f060f495536 | 116 | |
alex_20 | 9:6f060f495536 | 117 | case 1: // Actual Game |
alex_20 | 9:6f060f495536 | 118 | { |
alex_20 | 9:6f060f495536 | 119 | game.update(coord.x); |
alex_20 | 9:6f060f495536 | 120 | |
alex_20 | 9:6f060f495536 | 121 | // check if flag is set i.e. interrupt has occured |
alex_20 | 9:6f060f495536 | 122 | if (g_timer_flag) { |
alex_20 | 9:6f060f495536 | 123 | g_timer_flag = 0; // if it has, clear the flag |
alex_20 | 9:6f060f495536 | 124 | |
alex_20 | 9:6f060f495536 | 125 | // generate random road or ball |
alex_20 | 9:6f060f495536 | 126 | // initialize random seed: |
alex_20 | 9:6f060f495536 | 127 | srand (time(NULL)); |
alex_20 | 9:6f060f495536 | 128 | |
alex_20 | 9:6f060f495536 | 129 | number_1 = (rand() % 20 + 1) - 10; |
alex_20 | 9:6f060f495536 | 130 | number_2 = (static_cast <float> (rand()) / (static_cast <float> (RAND_MAX/10.0))) - 5.0; |
alex_20 | 9:6f060f495536 | 131 | game.reset_ball(); |
alex_20 | 6:40ef2030334c | 132 | } |
alex_20 | 9:6f060f495536 | 133 | |
alex_20 | 9:6f060f495536 | 134 | game.generate_ball(number_1); |
alex_20 | 9:6f060f495536 | 135 | game.generate_road(number_2); |
alex_20 | 9:6f060f495536 | 136 | |
alex_20 | 9:6f060f495536 | 137 | // put the MCU to sleep until an interrupt wakes it up |
alex_20 | 9:6f060f495536 | 138 | sleep(); |
alex_20 | 9:6f060f495536 | 139 | render(); |
alex_20 | 9:6f060f495536 | 140 | display_lives(lives); |
alex_20 | 9:6f060f495536 | 141 | |
alex_20 | 9:6f060f495536 | 142 | int collision = game.check_collision(); |
alex_20 | 9:6f060f495536 | 143 | if(collision == 1 && old_collision == 0) { |
alex_20 | 9:6f060f495536 | 144 | lives--; |
alex_20 | 9:6f060f495536 | 145 | if (lives == 0) { |
alex_20 | 9:6f060f495536 | 146 | state = 4; |
alex_20 | 9:6f060f495536 | 147 | } |
alex_20 | 9:6f060f495536 | 148 | } |
alex_20 | 9:6f060f495536 | 149 | |
alex_20 | 9:6f060f495536 | 150 | old_collision = collision; |
alex_20 | 9:6f060f495536 | 151 | |
alex_20 | 9:6f060f495536 | 152 | if(g_buttonA_flag) { |
alex_20 | 9:6f060f495536 | 153 | state = 3; |
alex_20 | 9:6f060f495536 | 154 | g_buttonA_flag = 0; |
alex_20 | 9:6f060f495536 | 155 | } |
alex_20 | 9:6f060f495536 | 156 | break; |
alex_20 | 9:6f060f495536 | 157 | } |
alex_20 | 9:6f060f495536 | 158 | |
alex_20 | 9:6f060f495536 | 159 | |
alex_20 | 9:6f060f495536 | 160 | case 2: // Instructions |
alex_20 | 9:6f060f495536 | 161 | { |
alex_20 | 9:6f060f495536 | 162 | instructions(); |
alex_20 | 9:6f060f495536 | 163 | if(g_buttonA_flag) { |
alex_20 | 9:6f060f495536 | 164 | state = 0; |
alex_20 | 9:6f060f495536 | 165 | g_buttonA_flag = 0; |
alex_20 | 6:40ef2030334c | 166 | } |
alex_20 | 9:6f060f495536 | 167 | break; |
alex_20 | 9:6f060f495536 | 168 | } |
alex_20 | 9:6f060f495536 | 169 | |
alex_20 | 9:6f060f495536 | 170 | |
alex_20 | 9:6f060f495536 | 171 | case 3: // Pause Menu |
alex_20 | 9:6f060f495536 | 172 | { |
alex_20 | 9:6f060f495536 | 173 | pause(); |
alex_20 | 9:6f060f495536 | 174 | if(coord.y >= 0.5) { |
alex_20 | 9:6f060f495536 | 175 | up_down = 17; |
alex_20 | 6:40ef2030334c | 176 | } |
alex_20 | 9:6f060f495536 | 177 | if(coord.y <= -0.5) { |
alex_20 | 9:6f060f495536 | 178 | up_down = 25; |
alex_20 | 9:6f060f495536 | 179 | } |
alex_20 | 9:6f060f495536 | 180 | |
alex_20 | 9:6f060f495536 | 181 | draw_arrow(2,up_down); |
alex_20 | 9:6f060f495536 | 182 | if(g_buttonA_flag) { |
alex_20 | 9:6f060f495536 | 183 | if(up_down == 17){ |
alex_20 | 9:6f060f495536 | 184 | state = 1; |
alex_20 | 9:6f060f495536 | 185 | g_buttonA_flag = 0; |
alex_20 | 9:6f060f495536 | 186 | } |
alex_20 | 9:6f060f495536 | 187 | if(up_down == 25) { |
alex_20 | 9:6f060f495536 | 188 | state = 0; |
alex_20 | 9:6f060f495536 | 189 | g_buttonA_flag = 0; |
alex_20 | 9:6f060f495536 | 190 | } |
alex_20 | 9:6f060f495536 | 191 | } |
alex_20 | 9:6f060f495536 | 192 | break; |
alex_20 | 9:6f060f495536 | 193 | } |
alex_20 | 9:6f060f495536 | 194 | |
alex_20 | 9:6f060f495536 | 195 | case 4: // Game over |
alex_20 | 9:6f060f495536 | 196 | { |
alex_20 | 9:6f060f495536 | 197 | game_over(); |
alex_20 | 9:6f060f495536 | 198 | if(g_buttonA_flag) { |
alex_20 | 9:6f060f495536 | 199 | state = 0; |
alex_20 | 9:6f060f495536 | 200 | g_buttonA_flag = 0; |
alex_20 | 9:6f060f495536 | 201 | } |
alex_20 | 9:6f060f495536 | 202 | break; |
alex_20 | 9:6f060f495536 | 203 | } |
alex_20 | 6:40ef2030334c | 204 | } |
alex_20 | 9:6f060f495536 | 205 | sleep(); |
alex_20 | 9:6f060f495536 | 206 | lcd.refresh(); |
eencae | 0:be41a15e7a86 | 207 | } |
eencae | 0:be41a15e7a86 | 208 | } |
alex_20 | 1:2ae7a8b01771 | 209 | |
alex_20 | 1:2ae7a8b01771 | 210 | void init_buttons() |
alex_20 | 1:2ae7a8b01771 | 211 | { |
alex_20 | 1:2ae7a8b01771 | 212 | // PCB has external pull-down resistors so turn the internal ones off |
alex_20 | 1:2ae7a8b01771 | 213 | // (default for DigitalIn) |
alex_20 | 1:2ae7a8b01771 | 214 | button_A.mode(PullNone); |
alex_20 | 9:6f060f495536 | 215 | button_B.mode(PullNone); |
alex_20 | 1:2ae7a8b01771 | 216 | button_C.mode(PullNone); |
alex_20 | 9:6f060f495536 | 217 | button_D.mode(PullNone); |
alex_20 | 9:6f060f495536 | 218 | } |
alex_20 | 9:6f060f495536 | 219 | |
alex_20 | 9:6f060f495536 | 220 | |
alex_20 | 9:6f060f495536 | 221 | void init() { |
alex_20 | 9:6f060f495536 | 222 | seven_seg.write(0x00); |
alex_20 | 9:6f060f495536 | 223 | lcd.init(); |
alex_20 | 9:6f060f495536 | 224 | joystick.init(); |
alex_20 | 9:6f060f495536 | 225 | lcd.setContrast(0.55); |
alex_20 | 9:6f060f495536 | 226 | |
alex_20 | 9:6f060f495536 | 227 | std::vector<Vector2Df> curve_point; |
alex_20 | 9:6f060f495536 | 228 | Vector2Df point = {-20.0,-50.0}; |
alex_20 | 9:6f060f495536 | 229 | curve_point.push_back(point); |
alex_20 | 9:6f060f495536 | 230 | |
alex_20 | 9:6f060f495536 | 231 | game.init(0.0, // road direction |
alex_20 | 9:6f060f495536 | 232 | 0.0, // road inclination |
alex_20 | 9:6f060f495536 | 233 | 4, // ball radius |
alex_20 | 9:6f060f495536 | 234 | curve_point, // points from ball path |
alex_20 | 9:6f060f495536 | 235 | 0, // car direction |
alex_20 | 9:6f060f495536 | 236 | 0, // car magnitude |
alex_20 | 9:6f060f495536 | 237 | 38.0, // car start position |
alex_20 | 9:6f060f495536 | 238 | 0.0, // offset |
alex_20 | 9:6f060f495536 | 239 | 0.0, // car_turn |
alex_20 | 9:6f060f495536 | 240 | 0.0); // ball_it |
alex_20 | 9:6f060f495536 | 241 | } |
alex_20 | 9:6f060f495536 | 242 | |
alex_20 | 9:6f060f495536 | 243 | void menu() { |
alex_20 | 9:6f060f495536 | 244 | lcd.printString("MAIN MENU",15,0); |
alex_20 | 9:6f060f495536 | 245 | lcd.printString("Start Game",10,2); |
alex_20 | 9:6f060f495536 | 246 | lcd.printString("Instructions",10,3); |
alex_20 | 9:6f060f495536 | 247 | lcd.printString("Press A",20,5); |
alex_20 | 9:6f060f495536 | 248 | } |
alex_20 | 9:6f060f495536 | 249 | |
alex_20 | 9:6f060f495536 | 250 | void instructions() { |
alex_20 | 9:6f060f495536 | 251 | lcd.printString("INSTRUCTIONS",6,0); |
alex_20 | 9:6f060f495536 | 252 | lcd.printString("Use joystick",6,1); |
alex_20 | 9:6f060f495536 | 253 | lcd.printString("to move",21,2); |
alex_20 | 9:6f060f495536 | 254 | lcd.printString("Press A to",13,4); |
alex_20 | 9:6f060f495536 | 255 | lcd.printString("pause or exit",4,5); |
alex_20 | 9:6f060f495536 | 256 | display_lives(0); |
alex_20 | 9:6f060f495536 | 257 | } |
alex_20 | 9:6f060f495536 | 258 | |
alex_20 | 9:6f060f495536 | 259 | void pause() { |
alex_20 | 9:6f060f495536 | 260 | lcd.printString("PAUSE",27,0); |
alex_20 | 9:6f060f495536 | 261 | lcd.printString("Back to game",10,2); |
alex_20 | 9:6f060f495536 | 262 | lcd.printString("Back to menu",10,3); |
alex_20 | 9:6f060f495536 | 263 | } |
alex_20 | 9:6f060f495536 | 264 | |
alex_20 | 9:6f060f495536 | 265 | void game_over() { |
alex_20 | 9:6f060f495536 | 266 | lcd.printString("GAME OVER",16,1); |
alex_20 | 9:6f060f495536 | 267 | lcd.printString("Don't collide!",1,2); |
alex_20 | 9:6f060f495536 | 268 | lcd.printString("Press A",20,5); |
alex_20 | 9:6f060f495536 | 269 | } |
alex_20 | 9:6f060f495536 | 270 | |
alex_20 | 9:6f060f495536 | 271 | void render() { |
alex_20 | 9:6f060f495536 | 272 | game.draw(lcd, utils); |
alex_20 | 9:6f060f495536 | 273 | } |
alex_20 | 9:6f060f495536 | 274 | |
alex_20 | 9:6f060f495536 | 275 | void draw_arrow(int x, int y) { |
alex_20 | 9:6f060f495536 | 276 | const int arrow[5][7] = { |
alex_20 | 9:6f060f495536 | 277 | { 0, 0, 0, 0, 1, 0, 0 }, |
alex_20 | 9:6f060f495536 | 278 | { 1, 1, 1, 1, 1, 1, 0 }, |
alex_20 | 9:6f060f495536 | 279 | { 1, 1, 1, 1, 1, 1, 1 }, |
alex_20 | 9:6f060f495536 | 280 | { 1, 1, 1, 1, 1, 1, 0 }, |
alex_20 | 9:6f060f495536 | 281 | { 0, 0, 0, 0, 1, 0, 0 }, |
alex_20 | 9:6f060f495536 | 282 | }; |
alex_20 | 9:6f060f495536 | 283 | lcd.drawSprite(x,y,5,7,(int *)arrow); |
alex_20 | 9:6f060f495536 | 284 | } |
alex_20 | 9:6f060f495536 | 285 | |
alex_20 | 9:6f060f495536 | 286 | // time-triggered interrupt |
alex_20 | 9:6f060f495536 | 287 | void timer_isr() { |
alex_20 | 9:6f060f495536 | 288 | g_timer_flag = 1; // set flag in ISR |
alex_20 | 9:6f060f495536 | 289 | } |
alex_20 | 9:6f060f495536 | 290 | |
alex_20 | 9:6f060f495536 | 291 | // Button A event-triggered interrupt |
alex_20 | 9:6f060f495536 | 292 | void buttonA_isr() { |
alex_20 | 9:6f060f495536 | 293 | g_buttonA_flag = 1; // set flag in ISR |
alex_20 | 9:6f060f495536 | 294 | } |
alex_20 | 9:6f060f495536 | 295 | |
alex_20 | 9:6f060f495536 | 296 | void display_lives(int lives) { |
alex_20 | 9:6f060f495536 | 297 | if (lives == 4) { |
alex_20 | 9:6f060f495536 | 298 | lcd.printString("Lives: 4",0,0); |
alex_20 | 9:6f060f495536 | 299 | seven_seg.write(0x66); |
alex_20 | 9:6f060f495536 | 300 | } else if (lives == 3) { |
alex_20 | 9:6f060f495536 | 301 | lcd.printString("Lives: 3",0,0); |
alex_20 | 9:6f060f495536 | 302 | seven_seg.write(0x4F); |
alex_20 | 9:6f060f495536 | 303 | } else if (lives == 2) { |
alex_20 | 9:6f060f495536 | 304 | lcd.printString("Lives: 2",0,0); |
alex_20 | 9:6f060f495536 | 305 | seven_seg.write(0x5B); |
alex_20 | 9:6f060f495536 | 306 | } else if (lives == 1) { |
alex_20 | 9:6f060f495536 | 307 | lcd.printString("Lives: 1",0,0); |
alex_20 | 9:6f060f495536 | 308 | seven_seg.write(0x06); |
alex_20 | 9:6f060f495536 | 309 | } else { |
alex_20 | 9:6f060f495536 | 310 | seven_seg.write(0x3F); |
alex_20 | 9:6f060f495536 | 311 | } |
alex_20 | 1:2ae7a8b01771 | 312 | } |