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 05:07:34 2020 +0000
Revision:
11:1fd8fca23375
Parent:
10:19b250c7de5e
Child:
13:1472c1637bfc
Helios A. Lyons 201239214;

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