Final Submission. I have read and agreed with Statement of Academic Integrity.
Embed:
(wiki syntax)
Show/hide line numbers
main.cpp
00001 /* 00002 ELEC2645 Embedded Systems Project 00003 School of Electronic & Electrical Engineering 00004 University of Leeds 00005 Name: Nicholas Wu 00006 Username: el18nw 00007 Student ID Number: 201275179 00008 Date: 22/5/2020 00009 */ 00010 00011 #include "mbed.h" 00012 #include "Gamepad.h" 00013 #include "N5110.h" 00014 #include "Bitmap.h" 00015 #include "Spaceship.h" 00016 00017 // IN-GAME DEBUGGING FLAGS 00018 bool DEBUG_menu = 0; 00019 bool sound_on = 1; 00020 // During development a mixture of serial port and in-game debugging methods 00021 // I found in-game debugging to be more interesting and accessible 00022 // Thus I had left these two debugging options here as examples 00023 00024 // OBJECTS 00025 Gamepad pad; 00026 N5110 lcd; 00027 Bitmap AGALAG(title, 10, 48); // name, y and x width 00028 Bitmap SHIP0(player_ship0, 9, 12); 00029 Bitmap SHIP1(player_ship1, 9, 12); 00030 Spaceship player; 00031 Spaceship enemy[2]; 00032 // Declaring enemies as an array makes adding more enemies simple 00033 // for this platform 2 allows for a playable framerate 00034 00035 // FUNCTIONS & RESPECTIVE VARIABLES 00036 void dramatic_print(string, int, int, int); 00037 int temptime = clock(); 00038 char g_buffer[14] = "0"; 00039 void title_sequence(); 00040 float g_x = 0, g_y = 0; 00041 void create_stars(); 00042 bool g_star_map[224][224]; 00043 bool check_stars(int, int); 00044 void print_background(float, float, float); 00045 void menu(); 00046 void help(); 00047 void ship_select(); 00048 int player_ship_type = 0; 00049 bool difficulty = 0; 00050 bool god_mode = 0; 00051 void print_menu(int, int); 00052 void level_system(); 00053 int points = 0; 00054 void engine_setup(bool); 00055 void engine(bool, float); 00056 void render_objects(int); 00057 void draw_enemy(int, int, int); 00058 void effects(bool, int, float); 00059 void sound(bool); 00060 bool explode(int, int); 00061 void print_tracker(int i); 00062 00063 int main() { 00064 lcd.init(); 00065 lcd.inverseMode(); 00066 lcd.setContrast(0.48); 00067 pad.init(); 00068 lcd.backLightOn(); 00069 create_stars(); 00070 title_sequence(); 00071 while (1) menu(); 00072 } 00073 00074 void menu() { 00075 int menu = 0, selection = 0; 00076 00077 while (1) { 00078 lcd.clear(); 00079 print_background(1, 0, 0); 00080 print_menu(menu, selection); 00081 AGALAG.render(lcd, 18, 5); 00082 00083 if (pad.B_held()) selection++; 00084 if (pad.X_held()) selection--; 00085 while (pad.B_held() || pad.X_held()); 00086 if (selection < 0) selection = 3; 00087 else if (selection > 3) selection = 0; 00088 if (DEBUG_menu) { 00089 sprintf(g_buffer, " %1i,%1i", menu, selection); 00090 lcd.printString(g_buffer, 0, 5); 00091 } 00092 lcd.refresh(); 00093 00094 if (pad.A_held() || pad.Y_held()) { 00095 if (menu == 3) { 00096 if (selection == 0) sound_on = !sound_on; 00097 if (selection == 1) points += 100;; 00098 if (selection == 2) DEBUG_menu = !DEBUG_menu; 00099 if (selection == 3) menu = 0; 00100 }else if (menu==1) { 00101 if (selection == 0) ship_select(); 00102 if (selection == 1) god_mode =! god_mode; 00103 if (selection == 2) difficulty =! difficulty; 00104 if (selection == 3) menu = 0; 00105 }else if (menu == 0) { 00106 if (selection == 0) level_system(); 00107 if (selection == 1) menu = 1; 00108 if (selection == 2) help(); 00109 if (selection == 3) menu = 3; 00110 } 00111 //this while statement captures the frame so no more than 00112 //one increment could happen with each button press 00113 while (pad.A_held() || pad.Y_held()); 00114 } 00115 } 00116 } 00117 00118 void print_menu(int menu, int selection) { 00119 //selected menu item is denoted as such: >example< 00120 if (menu == 0) { 00121 if (selection == 0) lcd.printString(">PLAY!<", 21, 2); 00122 else lcd.printString("PLAY!", 27, 2); 00123 if (selection == 1) lcd.printString(">OPTIONS<", 15, 3); 00124 else lcd.printString("OPTIONS", 21, 3); 00125 if (selection == 2) lcd.printString(">CONTROLS<", 12, 4); 00126 else lcd.printString("CONTROLS", 18, 4); 00127 if (selection == 3) lcd.printString(">DEBUG<", 21, 5); 00128 else lcd.printString("DEBUG", 27, 5); 00129 } 00130 if (menu == 1) { 00131 if (selection == 0) lcd.printString(">SHIP=0<", 18, 2); 00132 else lcd.printString("SHIP=0", 24, 2); 00133 lcd.printChar(player_ship_type+49, 54, 2); 00134 if (selection == 1) lcd.printString(">GOD MODE=0<", 6, 3); 00135 else lcd.printString("GOD MODE=0", 12, 3); 00136 lcd.printChar(48+god_mode, 66, 3); 00137 if (selection == 2) lcd.printString(">EASY MODE<", 9, 4); 00138 else lcd.printString("EASY MODE", 15, 4); 00139 if (difficulty) lcd.printString("HARD", 15, 4); 00140 if (selection == 3) lcd.printString(">BACK<", 24, 5); 00141 else lcd.printString("BACK", 30, 5); 00142 } 00143 if (menu==3) { 00144 if (selection == 0) lcd.printString(">SOUND=0<", 15, 2); 00145 else lcd.printString("SOUND=0", 21, 2); 00146 if (sound_on) lcd.printString("1", 57, 2); 00147 if (selection == 1) lcd.printString(">POINTS= 0<", 3, 3); 00148 else lcd.printString("POINTS= 0", 9, 3); 00149 sprintf(g_buffer, "%4i", points); 00150 lcd.printString(g_buffer, 52, 3); 00151 if (selection == 2) lcd.printString(">MENU DB=0<", 9, 4); 00152 else lcd.printString("MENU DB=0", 15, 4); 00153 if (DEBUG_menu) lcd.printString("1", 63, 4); 00154 if (selection == 3) lcd.printString(">BACK<", 24, 5); 00155 else lcd.printString("BACK", 30, 5); 00156 } 00157 } 00158 00159 void ship_select() { 00160 player_ship_type++; 00161 player_ship_type %= 2; 00162 //selects between one of two 00163 //ship types available 00164 } 00165 00166 void level_system() { 00167 while (!pad.start_held()) { 00168 create_stars(); 00169 engine_setup(int(points/400)); 00170 //passing the result of this division saves calculation during gameplay 00171 if (enemy[0].HP <= 0 && enemy[1].HP <= 0 && player.HP <= 0) { 00172 lcd.printString("<ACCEPTABLE>", 6, 4); 00173 } 00174 else if (player.HP <= 0) lcd.printString("<GAME OVER>", 9, 4); 00175 else if (enemy[0].HP <= 0 && enemy[1].HP <= 0) { 00176 lcd.printString("<NICE>", 24, 4); 00177 points += 100; 00178 if (difficulty) points += 100; 00179 if (points>400) points += 100; 00180 } else lcd.printString("<TOO SLOW>", 12, 4); 00181 //above checks victory conditions, extra points for difficulty 00182 sprintf(g_buffer, "SCORE:%4i", points); 00183 if (enemy[0].HP > 0 || enemy[1].HP > 0) points = 0; 00184 lcd.printString(g_buffer, 12, 5); 00185 lcd.refresh(); 00186 wait_ms(200); 00187 if (points <= 200) lcd.printString("<EXIT: START>", 6, 0); 00188 lcd.printString("<PLAY: ABXY>", 9, 1); 00189 lcd.refresh(); 00190 while (!pad.A_held() && !pad.B_held() && !pad.Y_held() 00191 && !pad.X_held() && !pad.start_held()); 00192 } 00193 pad.leds(0); 00194 } 00195 00196 void engine_setup (bool phase_two) { 00197 float HP_divider; 00198 //init_ship sets parameters - position, direction, 00199 //turn rate, acceleration, speed and hitpoints 00200 if (player_ship_type == 0) { 00201 player.init_ship(1, 0, 0, 0.05, 0.05, 0.7, 12); 00202 HP_divider = 0.5; //HP divider is used to display health on the LEDs 00203 } else { 00204 player.init_ship(1, 0, 0, 0.15, 0.15, 0.5, 20); 00205 HP_divider = 0.3; 00206 } //ship type 2: slower speed and fire rate, 00207 //faster turn rate, higher damage and more HP 00208 enemy[0].init_ship(40, -20+rand()%40, -1+rand()%2, 00209 0.06+0.00005*points, 0.08+0.0001*points, 0.7+0.001*points, 10); 00210 //additional maths in the enemy initialisation 00211 //to increase difficulty over time 00212 if (difficulty) for (int i = 0; i <= 1; i++) { 00213 enemy[i].init_ship(50+rand()%30, 10-20*i, 2-4*i, 00214 0.06+0.00005*points, 0.1+0.0001*points, 1+0.001*points, 10); 00215 } else if (phase_two) for (int i = 0; i <= 1; i++) { 00216 enemy[i].init_ship(50+rand()%30, 10-20*i, -1+2*i, 00217 0.05+0.00005*points, 0.05+0.0001*points, 0.4+0.001*points, 10); 00218 } 00219 engine(phase_two, HP_divider); 00220 } 00221 00222 void engine(bool phase_two, float HP_divider) { 00223 int end_delay = 40, time_limit = clock() + 6000; 00224 // end_delay lets the animation continue for about two seconds after death 00225 // time_limit keeps the game fun by forcing players to engage with the game 00226 00227 while (end_delay > 0 && clock() < time_limit) { 00228 Vector2D coord=pad.get_mapped_coord(); 00229 lcd.clear(); 00230 00231 for (int i = 0; i <= (difficulty || phase_two); i++) { 00232 if (enemy[i].HP > 0) { 00233 enemy[i].AI_controls(player.pos_x, player.pos_y, player.orientation); 00234 } 00235 if (enemy[i].check_hitbox(int(player.pos_x), int(player.pos_y), 4)) { 00236 player.HP--; 00237 } 00238 if (player.check_hitbox(int(enemy[i].pos_x), int(enemy[i].pos_y), 5)) { 00239 if (player_ship_type == 0)enemy[i].HP--; 00240 else enemy[i].HP -= 4; //cannon on ship1 does more damage 00241 } 00242 enemy[i].update(); 00243 }//change the for loop and object declaration to add more enemies 00244 00245 if (player_ship_type == 0) SHIP0.render(lcd, 60, 20); 00246 else SHIP1.render(lcd, 60, 20); 00247 00248 render_objects(phase_two || difficulty); 00249 effects(phase_two, time_limit, HP_divider); 00250 // effects control every other aspect of the gamepad output 00251 00252 if (player.HP > 0) { 00253 player.controls(player_ship_type, pad.Y_held(), 00254 pad.A_held(), pad.X_held(), pad.B_held(), coord.x, coord.y); 00255 } 00256 if ((enemy[0].HP<=0 && enemy[1].HP <= 0) || player.HP <= 0) end_delay--; 00257 if (god_mode) player.HP = 24; 00258 00259 lcd.refresh(); 00260 // I opted not to limit the frame rate of the game because 00261 // it will never render fast enough to be less fun 00262 // as if it was running at full speed 00263 } 00264 } 00265 00266 void render_objects(int extra_enemies) { 00267 int temp1, temp2; 00268 float sine = sin(player.orientation), cosine = cos(player.orientation); 00269 00270 for (int i = -16; i < 69; i++) { 00271 for (int ii = -24; ii < 32; ii++) { 00272 temp1 = int(player.pos_x + (i * cosine - ii * sine)); 00273 temp2 = int(player.pos_y + (ii * cosine + i * sine)); 00274 00275 if (temp1 == int(enemy[0].pos_x) 00276 && temp2 == int(enemy[0].pos_y)) { 00277 draw_enemy(i, ii, 0); 00278 #ifdef DEBUG_render 00279 printf("Found enemy0: %3.1f,%3.1f\n", 00280 enemy[0].pos_x, enemy[0].pos_y); 00281 #endif 00282 00283 } else if (extra_enemies && temp1 == int(enemy[1].pos_x) 00284 && temp2 == int(enemy[1].pos_y)) { 00285 draw_enemy(i, ii, 1); 00286 #ifdef DEBUG_render 00287 printf("Found enemy1: %3.1f,%3.1f\n", 00288 enemy[1].pos_x, enemy[1].pos_y); 00289 #endif 00290 } 00291 else if (check_stars(temp1, temp2) 00292 || player.check_bullets(temp1, temp2) 00293 || enemy[0].check_bullets(temp1, temp2) 00294 || enemy[1].check_bullets(temp1, temp2)){ 00295 lcd.setPixel(68-i, 24-ii, 1); 00296 } 00297 }// above finds the location of particles to render 00298 } 00299 } 00300 00301 void draw_enemy(int i, int j, int num) { 00302 float angle = player.orientation - enemy[num].orientation - 1.5708f; 00303 //this calculation finds what direction 00304 //to draw the enemy with respect to the player 00305 00306 if (enemy[num].HP > 0) { 00307 lcd.drawRect(63 - i, 32 - j, 12, 4, FILL_TRANSPARENT); //health bar 00308 lcd.drawRect(64 - i, 33 - j, enemy[num].HP, 2, FILL_BLACK); 00309 00310 if (j < 20) {//limits the game to drawing within bound of the screen 00311 lcd.drawCircle(68 - i, 24 - j, 5, FILL_BLACK); 00312 lcd.drawCircle(68 - i + 3 * sin(angle), 00313 24 - j + 3 * cos(angle), 2, FILL_WHITE); 00314 } 00315 } else if (explode (68 - i, 24 - j) && j < 20) { 00316 lcd.drawCircle(68 - i, 24 - j, 5, FILL_BLACK); 00317 lcd.drawCircle(68 - i + 3 * sin(angle), 00318 24 - j + 3 * cos(angle), 2, FILL_WHITE); 00319 } 00320 } 00321 00322 void effects(bool phase_two, int time_limit, float HP_divider) { 00323 if (enemy[0].HP > 0) print_tracker(0); 00324 if ((difficulty || phase_two) && (enemy[1].HP > 0)) print_tracker(1); 00325 if (pad.X_held() && clock() % 18 > 8) { 00326 for (int i = 0; i < 3; i++)lcd.setPixel(69 + i, 24 , 1); 00327 if (clock() % 9 > 3) { 00328 lcd.setPixel(72, 24, 1); 00329 lcd.setPixel(73, 24, 1); 00330 } 00331 } 00332 pad.leds(0); //LEDs display player health proportionally 00333 if (player.HP > 0) { 00334 for (int health = 0; health <= (HP_divider*player.HP); health++) { 00335 pad.led(health, 1); 00336 } 00337 } 00338 else explode(68, 24); 00339 if (sound_on) sound(difficulty || phase_two); 00340 sprintf(g_buffer, "%2i", (time_limit - clock()) / 100); 00341 lcd.printString(g_buffer, 12, 5); 00342 } 00343 00344 void print_tracker(int num) { 00345 float diff_x = enemy[num].pos_x-player.pos_x; 00346 if (abs(diff_x) < 0.0001f) diff_x = 0.0001f; 00347 00348 float angle_to_enemy = atan((enemy[num].pos_y - player.pos_y) / diff_x); 00349 if ((diff_x) > 0) angle_to_enemy += 3.1416f; 00350 00351 #ifdef DEBUG_tracker 00352 sprintf(g_buffer, "%1.3f", player.orientation); 00353 lcd.printString(g_buffer, 60, 0); 00354 sprintf(g_buffer, "%1.3f", angle_to_enemy - player.orientation); 00355 lcd.printString(g_buffer, 60, 1); 00356 #endif 00357 00358 float sine = sin(angle_to_enemy - player.orientation); 00359 float cosine = cos(angle_to_enemy - player.orientation); 00360 00361 lcd.setPixel(67 + 8 * cosine, 24 + 6 * sine, 1); 00362 lcd.setPixel(67 + 9 * cosine, 24 + 7 * sine, 1); 00363 lcd.setPixel(67 + 10 * cosine, 24 + 8 * sine, 1); 00364 lcd.setPixel(67 + 11 * cosine, 24 + 9 * sine, 1); 00365 lcd.setPixel(67 + 12 * cosine, 24 + 10 * sine, 1); 00366 lcd.setPixel(67 + 13 * cosine, 24 + 11 * sine, 1); 00367 lcd.drawCircle(67 + 9 * cosine, 24 + 7 * sine, 1, FILL_BLACK); 00368 lcd.drawCircle(67 + 10 * cosine, 24 + 8 * sine, 1, FILL_BLACK); 00369 } 00370 00371 void sound(bool extra_enemy) { 00372 //buzzers can only produce one tone at a time 00373 //this function prioritises some sounds over others 00374 if (player.HP <= 0) { 00375 player.explosion_FX--; 00376 } 00377 if (enemy[0].HP <= 0) { 00378 enemy[0].explosion_FX--; 00379 } 00380 if (extra_enemy && enemy[1].HP <= 0) { 00381 enemy[1].explosion_FX--; 00382 } 00383 //"explosion" is initialised as 8, and is decremented each frame 00384 //below if statement makes the explosion produce noise over 7 frames 00385 if ((player.explosion_FX < 8 && player.explosion_FX > 0) 00386 || (enemy[0].explosion_FX < 8 && enemy[0].explosion_FX > 0) 00387 || (extra_enemy && enemy[1].explosion_FX < 8 && enemy[1].explosion_FX > 0)) { 00388 pad.tone(1000, 0.1); 00389 } 00390 else if (player.gun_FX > 0) pad.tone(1600 - player.gun_FX * 200, 0.1); 00391 //the player's weapon takes priority for sound, after explosions 00392 else if (enemy[0].gun_FX > 0) pad.tone(5000, 0.1); 00393 else if (enemy[1].gun_FX > 0) pad.tone(5000, 0.1); 00394 00395 enemy[0].gun_FX--; 00396 enemy[1].gun_FX--; 00397 player.gun_FX--; 00398 //decrementing the FX variable makes sure the sound is not delayed 00399 //if the function is busy producing a different sound 00400 } 00401 00402 bool explode(int i, int j) { 00403 //a clock() with a modulo helps make the explosion more exciting 00404 //by forcing it to switch on and off over time 00405 if (clock() % 9 > 4) { 00406 for (int inc = 0; inc < 6; inc++) { 00407 float b = rand() % 63 / 10; 00408 int a = rand() % 6; 00409 int tempx = i - a * sin(b), tempy = j - a * cos(b); 00410 00411 if (tempy > 3 && i < 81) lcd.drawCircle(tempx, tempy, 2, FILL_BLACK); 00412 }//6 randomly placed white packets to simulate explosion 00413 return 0; 00414 }//boolean returns are used for switching a rendering function in draw_enemy(); 00415 return 1; 00416 } 00417 00418 void print_background(float screen_x, float screen_y, float screen_orientation) { 00419 int temp1, temp2; 00420 float sine = sin(screen_orientation), cosine = cos(screen_orientation); 00421 00422 for (int i = -16; i < 69; i++ ) { 00423 for (int ii = -24; ii < 25; ii++) { 00424 temp1 = int(screen_x + (i * cosine - ii * sine)); 00425 temp2 = int(screen_y + (ii * cosine + i * sine)) + 1; 00426 00427 if (check_stars(temp1, temp2)) lcd.setPixel(68 - i, 24 - ii, 1); 00428 } 00429 } 00430 } 00431 00432 bool check_stars(int temp1, int temp2) { 00433 while (temp1 < 0) temp1 += 224; 00434 temp1 = temp1 % 224; //this function tesselates the star map 00435 while (temp2 < 0) temp2 += 224; 00436 temp2 = temp2 % 224; 00437 00438 if (g_star_map[temp1][temp2])return 1; 00439 else return 0; 00440 } 00441 00442 void create_stars() {//the backhround is a boolean array 00443 for (int i = 0; i < 224; i++) { 00444 for (int ii = 0; ii < 224; ii++) g_star_map[i][ii] = 0; 00445 }//this clears the background for drawing a new one 00446 for (int i = 0; i < 224; i++) { 00447 for (int ii = 0; ii < 224; ii++) { 00448 if (rand() % 1000 > 985) g_star_map[i][ii] = 1; 00449 }//1.5% of the sky is stars! 00450 } 00451 } 00452 00453 void title_sequence() { 00454 temptime = clock(); 00455 00456 while ((clock() - temptime) < 40) { 00457 lcd.clear(); 00458 print_background(1, 0, 0); 00459 lcd.refresh(); 00460 } 00461 sprintf(g_buffer, "Space Combat"); 00462 00463 while (!pad.A_held() && !pad.B_held() && !pad.Y_held() 00464 && !pad.X_held() && !pad.start_held()) { 00465 AGALAG.render(lcd, 18, 12); 00466 if ((clock() - temptime) < 240) dramatic_print(g_buffer, 7, 3, 20); 00467 lcd.refresh(); 00468 } 00469 temptime = clock(); 00470 00471 while (clock() - temptime < 140 && !pad.start_held()) { 00472 lcd.clear(); 00473 print_background(1, 0, 0); 00474 AGALAG.render(lcd, 18, 11 - (clock() - temptime) / 20); 00475 lcd.refresh(); 00476 } 00477 } 00478 00479 void dramatic_print(string str, int offset, int line_num, int delay_time) { 00480 int i = ((clock() - temptime) / delay_time) % 14; 00481 00482 for (int ii = 0; ii <= i; ii++) { 00483 lcd.printChar(str[ii], 6 * ii + offset, line_num); 00484 } 00485 } 00486 00487 void help() { 00488 while (pad.A_held() || pad.Y_held()); 00489 pad.leds(1.0); 00490 00491 while (!pad.A_held() && !pad.B_held() && !pad.Y_held() 00492 && !pad.X_held() && !pad.start_held()) { 00493 Vector2D coord = pad.get_mapped_coord(); 00494 lcd.clear(); 00495 print_background(1, 0, 0); 00496 lcd.printString("<HP=LED>", 19, 0); 00497 lcd.printString("<SHOOT>", 0, 1); 00498 lcd.printString("<MOVE>", 45, 1); 00499 lcd.printString(">BACK<", 24, 5); 00500 lcd.drawCircle(63, 36, 3, FILL_TRANSPARENT); 00501 lcd.drawCircle(63, 20, 3, FILL_TRANSPARENT); 00502 lcd.drawCircle(53, 28, 3, FILL_TRANSPARENT); 00503 lcd.drawCircle(73, 28, 3, FILL_TRANSPARENT); 00504 lcd.drawCircle(20 + int(10 * coord.x), 00505 28 - int(10 * coord.y), 3, FILL_BLACK); 00506 00507 if (clock() % 4 > 1) lcd.drawCircle(20, 28, 12, FILL_TRANSPARENT); 00508 lcd.refresh(); 00509 } 00510 pad.leds(0); 00511 }
Generated on Tue Jul 12 2022 17:30:57 by
1.7.2