Helios Lyons 201239214

Dependencies:   mbed

Brief

My aim for this project was to create a FRDM K64F adapted version of the classic Space Invaders by Tomohiro Nishikado. The game itself has a number of clear features to implement;

  • Left to right movement for the player 'canon'
  • A fixed amount of player lives
  • Firing mechanics for both canon and invaders (hence collision systems)
  • Random firing from remaining invaders
  • Wave based combat

My own addition to these established ideas was Boss waves, featuring a single, larger sprite which fires at a faster interval than previous waves. The addition of a movement system using a basic for loop, as opposed to a velocity based system, will enhance the nostalgic feel of the game.

https://os.mbed.com/media/uploads/helioslyons/screenshot_2020-05-27_at_06.12.00.png

Gameplay

Movement is controlled with the joystick, moving the canon left or right. Fire by pressing A. Invaders spawn at set positions, but randomly fire at a set interval, which is higher for boss waves. Time is taken during each wave, and displayed at wave intervals, and if the play wins.

Controls are shown on the Gamepad below: (attribution: Craig A. Evans, ELEC2645 University of Leeds)

https://os.mbed.com/media/uploads/helioslyons/screenshot_2020-05-27_at_06.20.18.png

Committer:
helioslyons
Date:
Wed May 27 15:40:47 2020 +0000
Revision:
13:1472c1637bfc
Parent:
11:1fd8fca23375
Final Submission. I have read and agreed with Statement of Academic Integrity.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
helioslyons 9:6a245c8ce08e 1 #include "Battle.h"
helioslyons 10:19b250c7de5e 2 #include "Bullet.h"
helioslyons 10:19b250c7de5e 3 #include "Invader.h"
helioslyons 10:19b250c7de5e 4 #include "Army.h"
helioslyons 10:19b250c7de5e 5
helioslyons 10:19b250c7de5e 6 #include "N5110.h"
helioslyons 10:19b250c7de5e 7 #include "Gamepad.h"
helioslyons 10:19b250c7de5e 8
helioslyons 10:19b250c7de5e 9 #include <vector>
helioslyons 11:1fd8fca23375 10 #include <algorithm>
helioslyons 11:1fd8fca23375 11 #include <memory>
helioslyons 11:1fd8fca23375 12
helioslyons 11:1fd8fca23375 13 using namespace std;
helioslyons 11:1fd8fca23375 14
helioslyons 11:1fd8fca23375 15 Ticker t;
helioslyons 9:6a245c8ce08e 16
helioslyons 9:6a245c8ce08e 17 Battle::Battle()
helioslyons 9:6a245c8ce08e 18 {
helioslyons 9:6a245c8ce08e 19
helioslyons 9:6a245c8ce08e 20 }
helioslyons 9:6a245c8ce08e 21
helioslyons 9:6a245c8ce08e 22 Battle::~Battle()
helioslyons 9:6a245c8ce08e 23 {
helioslyons 9:6a245c8ce08e 24
helioslyons 9:6a245c8ce08e 25 }
helioslyons 9:6a245c8ce08e 26
helioslyons 11:1fd8fca23375 27 void Battle::init(int rows, int columns, int speed, int interval, bool boss, int bossNum)
helioslyons 9:6a245c8ce08e 28 {
helioslyons 11:1fd8fca23375 29 reset(); // reset initial variables
helioslyons 11:1fd8fca23375 30 _speed = speed; // set value for accessible variables
helioslyons 10:19b250c7de5e 31 _interval = interval;
helioslyons 11:1fd8fca23375 32 _boss = boss;
helioslyons 11:1fd8fca23375 33 _rows = rows;
helioslyons 11:1fd8fca23375 34 _columns = columns;
helioslyons 11:1fd8fca23375 35
helioslyons 11:1fd8fca23375 36 _canon.init(HEIGHT - 3); // initialise player
helioslyons 11:1fd8fca23375 37 _army.create_army(_rows,_columns,speed,_boss,bossNum); // initialise army
helioslyons 9:6a245c8ce08e 38 }
helioslyons 9:6a245c8ce08e 39
helioslyons 9:6a245c8ce08e 40 void Battle::read_input(Gamepad &pad)
helioslyons 9:6a245c8ce08e 41 {
helioslyons 10:19b250c7de5e 42 _d = pad.get_direction();
helioslyons 10:19b250c7de5e 43 _mag = pad.get_mag();
helioslyons 10:19b250c7de5e 44 }
helioslyons 10:19b250c7de5e 45
helioslyons 11:1fd8fca23375 46 void Battle::draw(N5110 &lcd) // draw battle elements, and refresh display
helioslyons 10:19b250c7de5e 47 {
helioslyons 11:1fd8fca23375 48 _canon.draw(lcd); // draw canon
helioslyons 11:1fd8fca23375 49 _army.draw(lcd); // draw invaders
helioslyons 10:19b250c7de5e 50
helioslyons 11:1fd8fca23375 51 for(int i = 0; i < Bombs.size(); i++) { Bombs[i]->draw(lcd); } // draw bombs
helioslyons 11:1fd8fca23375 52 for(int n = 0; n < Bullets.size(); n++) { Bullets[n]->draw(lcd); } // draw bullets
helioslyons 11:1fd8fca23375 53 lcd.refresh();
helioslyons 10:19b250c7de5e 54 }
helioslyons 10:19b250c7de5e 55
helioslyons 13:1472c1637bfc 56 void Battle::clock(Gamepad &pad) // call invader fire at _interval passed into battle.h
helioslyons 10:19b250c7de5e 57 {
helioslyons 11:1fd8fca23375 58 t.attach(Callback<void()>(this, &Battle::invader_fire), _interval);
helioslyons 11:1fd8fca23375 59 }
helioslyons 11:1fd8fca23375 60
helioslyons 11:1fd8fca23375 61 void Battle::update(Gamepad &pad) // update canon position and firing,
helioslyons 11:1fd8fca23375 62 { // and all projectile positions and collisions
helioslyons 11:1fd8fca23375 63 //_army.move_army();
helioslyons 11:1fd8fca23375 64 canon_fire(pad);
helioslyons 10:19b250c7de5e 65 _canon.update(_d,_mag);
helioslyons 10:19b250c7de5e 66
helioslyons 11:1fd8fca23375 67 for(int i = 0; i < Bombs.size(); i++) { Bombs[i]->update(); }
helioslyons 11:1fd8fca23375 68 for(int n = 0; n < Bullets.size(); n++) { Bullets[n]->update(); }
helioslyons 10:19b250c7de5e 69
helioslyons 11:1fd8fca23375 70 projectile_edge();
helioslyons 11:1fd8fca23375 71 bullet_collision(pad);
helioslyons 11:1fd8fca23375 72 bomb_collision(pad);
helioslyons 9:6a245c8ce08e 73 }
helioslyons 9:6a245c8ce08e 74
helioslyons 11:1fd8fca23375 75 void Battle::invader_fire() // pseudo-randomly select the position of an alive invader
helioslyons 11:1fd8fca23375 76 { // and drop a bomb
helioslyons 11:1fd8fca23375 77 std::vector<int> fire_pos_y;
helioslyons 11:1fd8fca23375 78 std::vector<int> fire_pos_x;
helioslyons 10:19b250c7de5e 79
helioslyons 11:1fd8fca23375 80 for (int i = 0; i < _rows; i++) { // iterate through invaders
helioslyons 11:1fd8fca23375 81 for (int n = 0; n < _columns; n++) {
helioslyons 11:1fd8fca23375 82 int state = invaders[i][n]->get_death();
helioslyons 11:1fd8fca23375 83 if (!state) { // add alive invader position to X and Y vectors
helioslyons 11:1fd8fca23375 84 Vector2D fire_pos = invaders[i][n]->get_pos();
helioslyons 11:1fd8fca23375 85 fire_pos_y.push_back(fire_pos.y);
helioslyons 11:1fd8fca23375 86 fire_pos_x.push_back(fire_pos.x);
helioslyons 11:1fd8fca23375 87 }
helioslyons 11:1fd8fca23375 88 }
helioslyons 11:1fd8fca23375 89 }
helioslyons 11:1fd8fca23375 90 // select random X and Y positions from the vectors
helioslyons 11:1fd8fca23375 91 int rand1 = rand() % fire_pos_y.size();
helioslyons 11:1fd8fca23375 92 int rand2 = rand() % fire_pos_x.size();
helioslyons 10:19b250c7de5e 93
helioslyons 11:1fd8fca23375 94 Vector2D fire_pos;
helioslyons 11:1fd8fca23375 95 fire_pos.y = fire_pos_y[rand1];
helioslyons 11:1fd8fca23375 96 fire_pos.x = fire_pos_x[rand2];
helioslyons 10:19b250c7de5e 97
helioslyons 11:1fd8fca23375 98 // instantiate a new Bullet pointer, and add it to the Bomb vector
helioslyons 11:1fd8fca23375 99 Bombs.push_back(new Bullet()); // initialise the Bullet with owner, position, speed
helioslyons 11:1fd8fca23375 100 Bombs.back()->init(false, fire_pos.x + 4, fire_pos.y + 2, 3);
helioslyons 9:6a245c8ce08e 101 }
helioslyons 9:6a245c8ce08e 102
helioslyons 11:1fd8fca23375 103 void Battle::canon_fire(Gamepad &pad) // fire when A is pressed, and add a new
helioslyons 11:1fd8fca23375 104 { // projectile to the player bullet vector
helioslyons 10:19b250c7de5e 105 bool A = pad.A_pressed();
helioslyons 10:19b250c7de5e 106 if (A) {
helioslyons 11:1fd8fca23375 107 Vector2D canon_pos = _canon.get_pos();
helioslyons 11:1fd8fca23375 108 int x1 = canon_pos.x + 2;
helioslyons 11:1fd8fca23375 109 int y1 = canon_pos.y + 1;
helioslyons 10:19b250c7de5e 110
helioslyons 11:1fd8fca23375 111 Bullets.push_back(new Bullet());
helioslyons 11:1fd8fca23375 112 Bullets.back()->init(true, x1, y1, 3);
helioslyons 10:19b250c7de5e 113
helioslyons 11:1fd8fca23375 114 printf("Player fired");
helioslyons 10:19b250c7de5e 115 }
helioslyons 9:6a245c8ce08e 116 }
helioslyons 9:6a245c8ce08e 117
helioslyons 11:1fd8fca23375 118 void Battle::bullet_collision(Gamepad &pad) // bullet collision algorithm
helioslyons 9:6a245c8ce08e 119 {
helioslyons 11:1fd8fca23375 120 Vector2D BulPos;
helioslyons 11:1fd8fca23375 121
helioslyons 11:1fd8fca23375 122 for(int z = 0; z < Bullets.size(); z++) { // iterate through bullets
helioslyons 11:1fd8fca23375 123 BulPos = Bullets[z]->get_pos(); // fetch each bullets position
helioslyons 11:1fd8fca23375 124 Rectangle bulletRect(BulPos.x, BulPos.y, 1, 2); // apply to rectangle
helioslyons 11:1fd8fca23375 125
helioslyons 11:1fd8fca23375 126 for (int i = 0; i < _rows; i++) { // iterate through invaders
helioslyons 11:1fd8fca23375 127 for (int n = 0; n < _columns; n++) {
helioslyons 11:1fd8fca23375 128
helioslyons 11:1fd8fca23375 129 bool state = invaders[i][n]->get_death();
helioslyons 10:19b250c7de5e 130
helioslyons 11:1fd8fca23375 131 if (!state) { // only test for collision if invader is alive
helioslyons 11:1fd8fca23375 132 Vector2D invPos = invaders[i][n]->get_pos(); // fetch parameters
helioslyons 11:1fd8fca23375 133 int invWidth = invaders[i][n]->get_width();
helioslyons 11:1fd8fca23375 134 int invHeight = invaders[i][n]->get_height();
helioslyons 11:1fd8fca23375 135 Rectangle invaderRect(invPos.x, invPos.y, invWidth, invHeight); // apply to rectangle
helioslyons 11:1fd8fca23375 136
helioslyons 11:1fd8fca23375 137 if(colTest(invaderRect, bulletRect)){ // compare invader vs. bullet rectangles
helioslyons 11:1fd8fca23375 138
helioslyons 11:1fd8fca23375 139 invaders[i][n]->hit(); // if collided, decrease health
helioslyons 11:1fd8fca23375 140 //int sprite = invaders[i][n]->get_sprite(); // add to kill count
helioslyons 11:1fd8fca23375 141 //_canon.kill(sprite);
helioslyons 11:1fd8fca23375 142 pad.tone(750.0,0.1); // play a tone
helioslyons 11:1fd8fca23375 143 Bullets.erase(Bullets.begin() + z - 1); // and delete bullet
helioslyons 11:1fd8fca23375 144 }
helioslyons 11:1fd8fca23375 145 }
helioslyons 11:1fd8fca23375 146 }
helioslyons 11:1fd8fca23375 147 }
helioslyons 10:19b250c7de5e 148 }
helioslyons 9:6a245c8ce08e 149 }
helioslyons 9:6a245c8ce08e 150
helioslyons 11:1fd8fca23375 151 void Battle::bomb_collision(Gamepad &pad) // check if bombs have collided with the canon
helioslyons 11:1fd8fca23375 152 { // if so, remove life, play tone,
helioslyons 11:1fd8fca23375 153 Vector2D BombPos; // and delete from vector
helioslyons 10:19b250c7de5e 154 Vector2D canonPos;
helioslyons 10:19b250c7de5e 155
helioslyons 11:1fd8fca23375 156 for (int i = 0; i < Bombs.size(); i++) { // check
helioslyons 11:1fd8fca23375 157 BombPos = Bombs[i]->get_pos();
helioslyons 11:1fd8fca23375 158
helioslyons 11:1fd8fca23375 159 canonPos = _canon.get_pos();
helioslyons 11:1fd8fca23375 160 int canonWidth = _canon.get_width();
helioslyons 11:1fd8fca23375 161 int canonHeight = _canon.get_height();
helioslyons 10:19b250c7de5e 162
helioslyons 11:1fd8fca23375 163 Rectangle canonRect(canonPos.x, canonPos.y, canonWidth, canonHeight);
helioslyons 11:1fd8fca23375 164 Rectangle bombRect(BombPos.x, BombPos.y, 2, 2);
helioslyons 10:19b250c7de5e 165
helioslyons 11:1fd8fca23375 166 if (colTest(canonRect,bombRect)) {
helioslyons 10:19b250c7de5e 167 _canon.remove_life();
helioslyons 11:1fd8fca23375 168 pad.tone(650.0,0.1);
helioslyons 11:1fd8fca23375 169 Bombs.erase(Bombs.begin() + i - 1);
helioslyons 10:19b250c7de5e 170 }
helioslyons 10:19b250c7de5e 171 }
helioslyons 9:6a245c8ce08e 172 }
helioslyons 9:6a245c8ce08e 173
helioslyons 11:1fd8fca23375 174 void Battle::projectile_edge() // if projectiles are beyond screen limits
helioslyons 11:1fd8fca23375 175 { // remove from the vector to free memory
helioslyons 11:1fd8fca23375 176 Vector2D BombEdge;
helioslyons 11:1fd8fca23375 177 Vector2D BullEdge;
helioslyons 10:19b250c7de5e 178
helioslyons 11:1fd8fca23375 179 for(int i = 0; i < Bombs.size(); i++) {
helioslyons 11:1fd8fca23375 180 BombEdge = Bombs[i]->get_pos();
helioslyons 11:1fd8fca23375 181 if (BombEdge.y >= HEIGHT){
helioslyons 11:1fd8fca23375 182 Bombs.erase(Bombs.begin() + i - 1);
helioslyons 10:19b250c7de5e 183 }
helioslyons 10:19b250c7de5e 184 }
helioslyons 11:1fd8fca23375 185
helioslyons 11:1fd8fca23375 186 for(int n = 0; n < Bullets.size(); n++) {
helioslyons 11:1fd8fca23375 187 BullEdge = Bullets[n]->get_pos();
helioslyons 11:1fd8fca23375 188 if (BullEdge.y <= 0){
helioslyons 11:1fd8fca23375 189 Bullets.erase(Bullets.begin() + n - 1);
helioslyons 10:19b250c7de5e 190 }
helioslyons 10:19b250c7de5e 191 }
helioslyons 9:6a245c8ce08e 192 }
helioslyons 9:6a245c8ce08e 193
helioslyons 11:1fd8fca23375 194 bool Battle::end() // if all the invaders are dead, returns true
helioslyons 10:19b250c7de5e 195 {
helioslyons 11:1fd8fca23375 196 if (_army.end()) { // saves main from interacting with army.h
helioslyons 11:1fd8fca23375 197 return _end = true;
helioslyons 11:1fd8fca23375 198 }
helioslyons 11:1fd8fca23375 199 else { return _end = false; }
helioslyons 11:1fd8fca23375 200 }
helioslyons 11:1fd8fca23375 201
helioslyons 11:1fd8fca23375 202 void Battle::reset() // free memory from the vector pointers for projectiles
helioslyons 11:1fd8fca23375 203 {
helioslyons 11:1fd8fca23375 204 Bullets.clear();
helioslyons 11:1fd8fca23375 205 Bombs.clear();
helioslyons 11:1fd8fca23375 206
helioslyons 11:1fd8fca23375 207 _end = false; // reset _end and _dead conditions
helioslyons 11:1fd8fca23375 208 _dead = false;
helioslyons 11:1fd8fca23375 209 }
helioslyons 11:1fd8fca23375 210
helioslyons 11:1fd8fca23375 211 int Battle::life() // check next comment
helioslyons 11:1fd8fca23375 212 {
helioslyons 11:1fd8fca23375 213 int lives = _canon.get_life();
helioslyons 11:1fd8fca23375 214 return lives;
helioslyons 11:1fd8fca23375 215 }
helioslyons 11:1fd8fca23375 216
helioslyons 11:1fd8fca23375 217 void Battle::reset_life() // pulls from canon.h method so that main only
helioslyons 11:1fd8fca23375 218 { // has to interact with battle.h
helioslyons 11:1fd8fca23375 219 _canon.reset_life();
helioslyons 11:1fd8fca23375 220 }
helioslyons 11:1fd8fca23375 221
helioslyons 11:1fd8fca23375 222 bool Battle::colTest(Rectangle r1, Rectangle r2) // check if 2 rectangles
helioslyons 11:1fd8fca23375 223 { // are overlapping
helioslyons 11:1fd8fca23375 224 return ((r1.x + r1.width > r2.x) // code taken from Mozilla:
helioslyons 11:1fd8fca23375 225 && (r1.x < r2.x + r2.width) // https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
helioslyons 11:1fd8fca23375 226 && (r1.y + r2.height > r2.y) // for 2D game collisions
helioslyons 11:1fd8fca23375 227 && (r1.y < r2.y + r2.height));
helioslyons 10:19b250c7de5e 228 }