A simple one-level platform game. Developed as part of ELEC2645 at University of Leeds, spring 2015.

Dependencies:   N5110 PinDetect PowerControl mbed

An ARM mbed LPC1768 microcontroller have been used to develop a handheld arcade game in the style of an old-school platformer. This project is entirely my own independent work in all stages of the development; including design, defining project specifications, breadboard prototyping, schematic and PCB layout using CAD, assembly, testing and software development. Due to this being part of the ELEC2645 Embedded Systems Project module at University of Leeds, spring 2015, limitations were given on the available hardware components. Credit is due to the authors of the dependent libraries (N5110, Pin Detect, PowerControl and mbed). I would also like to thank the author of Game Programming Patterns as well as the authors of SFML Game Development for providing me with useful sources for programming design patterns.

/media/uploads/Siriagus/game_assembled.jpg

Project aims

  • Implement simple gameplay:
    • A single, fixed (no scrolling) level.
    • Player can move left to right, jump and shoot.
    • Enemies will drop from the top of the screen.
    • The player gets points for shooting enemies.
    • The player dies when it gets hits by an enemy.
  • Implement a simple menu system.
  • Enable the user to adjust the brightness of the display.
  • Output sound to enhance the user experience.

Software

The program flow is controlled by a finite state machine. The implemented design was inspired by the State design pattern from the books Game Programming Patterns and SFML Game Development. The StateManager class is responsible for updating and rendering the current selected state. It also changes the state based on request from the current state. The framework built for the state machine used in this project makes it easy to add new screens. The different main states (indicated by the background colour) and how the user interaction is shown below: /media/uploads/Siriagus/arcadegameuserinteraction.png

Hardware

Schematic:

/media/uploads/Siriagus/schematic.png

Printed circuit board (PCB):

/media/uploads/Siriagus/pcb.png

Images

A seperate program was written to convert images (png) to text-representation of the maps. Enemies and numbers on the screen are also collected from a sprite-sheet created in the same manner.

/media/uploads/Siriagus/unileeds3.png /media/uploads/Siriagus/newmap2.png

Committer:
Siriagus
Date:
Sun May 10 13:14:33 2015 +0000
Revision:
16:caf613d5b85e
Parent:
15:d5eb13c4c1c6
Child:
17:d6a3b29cab31
Added wrapping (when entity goes outside the map, it enters on the other side).

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Siriagus 7:678873947b29 1 #include "Game.h"
Siriagus 7:678873947b29 2
Siriagus 8:9ac6a428fa26 3 Serial pc(USBTX, USBRX); // TO DELETE - DEBUGGING ONLY
Siriagus 8:9ac6a428fa26 4
Siriagus 8:9ac6a428fa26 5 void Game::init()
Siriagus 16:caf613d5b85e 6 {
Siriagus 15:d5eb13c4c1c6 7 paused = false;
Siriagus 15:d5eb13c4c1c6 8
Siriagus 12:8178fad5e660 9 // Set initial values for the player
Siriagus 13:7ab71c7c311b 10 player.x = 40; // start x
Siriagus 13:7ab71c7c311b 11 player.y = 5; // start y
Siriagus 13:7ab71c7c311b 12 player.width = player.height = 5; // important that this is correct, see size of Image::Player in Resources.h
Siriagus 12:8178fad5e660 13 player.onGround = false;
Siriagus 16:caf613d5b85e 14 }
Siriagus 16:caf613d5b85e 15
Siriagus 16:caf613d5b85e 16 void Game::spawnEnemy()
Siriagus 16:caf613d5b85e 17 {
Siriagus 16:caf613d5b85e 18 Enemy *enemy = new Enemy(75, 3, 5, 5, Enemy::SIMPLE);
Siriagus 16:caf613d5b85e 19 enemy->vx = -1;
Siriagus 15:d5eb13c4c1c6 20 enemy->facingLeft = true;
Siriagus 16:caf613d5b85e 21 enemies.push_back(enemy);
Siriagus 8:9ac6a428fa26 22 }
Siriagus 8:9ac6a428fa26 23
Siriagus 8:9ac6a428fa26 24 // Functions
Siriagus 7:678873947b29 25 void Game::update(float dt)
Siriagus 16:caf613d5b85e 26 {
Siriagus 15:d5eb13c4c1c6 27 // Pause button input
Siriagus 15:d5eb13c4c1c6 28 if (input->read(Input::ButtonC))
Siriagus 15:d5eb13c4c1c6 29 {
Siriagus 15:d5eb13c4c1c6 30 if (releasedBtnC)
Siriagus 15:d5eb13c4c1c6 31 {
Siriagus 15:d5eb13c4c1c6 32 paused = !paused;
Siriagus 15:d5eb13c4c1c6 33 releasedBtnC = false;
Siriagus 15:d5eb13c4c1c6 34 }
Siriagus 15:d5eb13c4c1c6 35 }
Siriagus 15:d5eb13c4c1c6 36 else
Siriagus 15:d5eb13c4c1c6 37 releasedBtnC = true;
Siriagus 15:d5eb13c4c1c6 38
Siriagus 15:d5eb13c4c1c6 39 // Skip the rest if paused
Siriagus 15:d5eb13c4c1c6 40 if (paused) return;
Siriagus 15:d5eb13c4c1c6 41
Siriagus 15:d5eb13c4c1c6 42
Siriagus 16:caf613d5b85e 43 if ((rand() % 100) > 93)
Siriagus 16:caf613d5b85e 44 spawnEnemy();
Siriagus 16:caf613d5b85e 45
Siriagus 8:9ac6a428fa26 46 // Handle input, should be its own function
Siriagus 8:9ac6a428fa26 47 switch(input->joystick->getDirection())
Siriagus 8:9ac6a428fa26 48 {
Siriagus 8:9ac6a428fa26 49 case LEFT:
Siriagus 8:9ac6a428fa26 50 case UP_LEFT:
Siriagus 8:9ac6a428fa26 51 case DOWN_LEFT:
Siriagus 8:9ac6a428fa26 52 player.vx = -2;
Siriagus 11:adb68da98262 53 player.facingLeft = true;
Siriagus 8:9ac6a428fa26 54 break;
Siriagus 8:9ac6a428fa26 55
Siriagus 8:9ac6a428fa26 56 case RIGHT:
Siriagus 8:9ac6a428fa26 57 case UP_RIGHT:
Siriagus 8:9ac6a428fa26 58 case DOWN_RIGHT:
Siriagus 8:9ac6a428fa26 59 player.vx = 2;
Siriagus 11:adb68da98262 60 player.facingLeft = false;
Siriagus 8:9ac6a428fa26 61 break;
Siriagus 8:9ac6a428fa26 62
Siriagus 8:9ac6a428fa26 63 case CENTER:
Siriagus 8:9ac6a428fa26 64 player.vx = 0;
Siriagus 8:9ac6a428fa26 65 break;
Siriagus 8:9ac6a428fa26 66 }
Siriagus 7:678873947b29 67
Siriagus 16:caf613d5b85e 68
Siriagus 16:caf613d5b85e 69 // MOVE: Random enemies
Siriagus 13:7ab71c7c311b 70
Siriagus 13:7ab71c7c311b 71 // Gravity
Siriagus 11:adb68da98262 72 player.vy += 1;
Siriagus 8:9ac6a428fa26 73
Siriagus 11:adb68da98262 74 // Check if player is trying to jump. Player can only jump if it's on the ground
Siriagus 12:8178fad5e660 75 if (input->read(Input::ButtonA) && player.onGround)
Siriagus 8:9ac6a428fa26 76 {
Siriagus 8:9ac6a428fa26 77 player.vy = -4;
Siriagus 12:8178fad5e660 78 player.onGround = false;
Siriagus 8:9ac6a428fa26 79 }
Siriagus 8:9ac6a428fa26 80
Siriagus 11:adb68da98262 81 // Terminal velocity 3 px/update
Siriagus 14:b4fed570abaf 82 if (player.vy > TERMINAL_VELOCITY) player.vy = TERMINAL_VELOCITY;
Siriagus 8:9ac6a428fa26 83
Siriagus 13:7ab71c7c311b 84 moveWithCollisionTest(&player, map);
Siriagus 16:caf613d5b85e 85 moveEnemies();
Siriagus 11:adb68da98262 86
Siriagus 16:caf613d5b85e 87
Siriagus 16:caf613d5b85e 88 // else if (enemy->y >= HEIGHT) ; // TODO : delete enemy
Siriagus 8:9ac6a428fa26 89
Siriagus 11:adb68da98262 90 // Check if bullet should be fired
Siriagus 9:da608ae65df9 91 if (input->read(Input::ButtonB) && releasedBtnB)
Siriagus 8:9ac6a428fa26 92 {
Siriagus 11:adb68da98262 93 // Create a new bullet and give it initial values
Siriagus 8:9ac6a428fa26 94 Point* bullet = new Point;
Siriagus 15:d5eb13c4c1c6 95 bullet->x = (int)(player.x + (player.width / 2)); //(player.facingLeft) ? (player.x-1) : (player.x + player.width);
Siriagus 8:9ac6a428fa26 96 bullet->y = player.y + 2;
Siriagus 11:adb68da98262 97 bullet->vx = (player.facingLeft) ? -4 : 4;
Siriagus 9:da608ae65df9 98 bullet->vy = 0;
Siriagus 8:9ac6a428fa26 99
Siriagus 8:9ac6a428fa26 100 bullets.push_back(bullet);
Siriagus 9:da608ae65df9 101 releasedBtnB = false;
Siriagus 8:9ac6a428fa26 102 }
Siriagus 9:da608ae65df9 103 else if (!input->read(Input::ButtonB))
Siriagus 9:da608ae65df9 104 releasedBtnB = true;
Siriagus 8:9ac6a428fa26 105
Siriagus 15:d5eb13c4c1c6 106 // Loop through bullets and move them + collision test
Siriagus 9:da608ae65df9 107 for (std::vector<Point*>::iterator it = bullets.begin(); it != bullets.end();)
Siriagus 8:9ac6a428fa26 108 {
Siriagus 15:d5eb13c4c1c6 109 Point* bullet = *it;
Siriagus 15:d5eb13c4c1c6 110
Siriagus 15:d5eb13c4c1c6 111 int x0; // left border of collision rect
Siriagus 15:d5eb13c4c1c6 112 int x1; // right border of collision rect
Siriagus 15:d5eb13c4c1c6 113
Siriagus 15:d5eb13c4c1c6 114 int oldX = bullet->x;
Siriagus 15:d5eb13c4c1c6 115 int newX = bullet->x + bullet->vx;
Siriagus 15:d5eb13c4c1c6 116
Siriagus 15:d5eb13c4c1c6 117 x0 = min(oldX, newX);
Siriagus 15:d5eb13c4c1c6 118 x1 = max(oldX, newX);
Siriagus 15:d5eb13c4c1c6 119
Siriagus 15:d5eb13c4c1c6 120 // Collision rect for bullet in this time step
Siriagus 15:d5eb13c4c1c6 121 Rectangle bulletColRect(x0, bullet->y, (x1-x0)+1, 1);
Siriagus 9:da608ae65df9 122
Siriagus 15:d5eb13c4c1c6 123 bool col = false;
Siriagus 15:d5eb13c4c1c6 124 // Delete if outside screen
Siriagus 15:d5eb13c4c1c6 125 if (newX < 0 || newX > WIDTH || bulletHitMap(bulletColRect, map)) // if outside screen
Siriagus 15:d5eb13c4c1c6 126 {
Siriagus 15:d5eb13c4c1c6 127 col = true;
Siriagus 15:d5eb13c4c1c6 128 }
Siriagus 15:d5eb13c4c1c6 129 else
Siriagus 9:da608ae65df9 130 {
Siriagus 15:d5eb13c4c1c6 131 // loop through all enemies
Siriagus 15:d5eb13c4c1c6 132 //bullet // point
Siriagus 15:d5eb13c4c1c6 133 //enemy // entity
Siriagus 16:caf613d5b85e 134 for (std::vector<Enemy*>::iterator it = enemies.begin(); it != enemies.end(); ++it)
Siriagus 15:d5eb13c4c1c6 135 {
Siriagus 16:caf613d5b85e 136 Enemy *enemy = *it;
Siriagus 15:d5eb13c4c1c6 137
Siriagus 16:caf613d5b85e 138 if (!enemy->dead && bullet->x >= enemy->x && bullet->x <= enemy->getRight() && bullet->y >= enemy->y && bullet->y <= enemy->getBottom())
Siriagus 16:caf613d5b85e 139 {
Siriagus 16:caf613d5b85e 140 col = true;
Siriagus 16:caf613d5b85e 141 // break; // todo: make for loop - iterate through all enemies
Siriagus 16:caf613d5b85e 142 enemy->vx = bullet->vx / 2; // sends the dead enemy in the same direction as the incoming bullet
Siriagus 16:caf613d5b85e 143 enemy->vy = -3; // sends the dead enemy upwards in the air, because of impact
Siriagus 16:caf613d5b85e 144
Siriagus 16:caf613d5b85e 145 enemy->dead = true;
Siriagus 16:caf613d5b85e 146 // remove enemy - todo: make vector
Siriagus 16:caf613d5b85e 147 }
Siriagus 15:d5eb13c4c1c6 148 }
Siriagus 15:d5eb13c4c1c6 149 }
Siriagus 15:d5eb13c4c1c6 150
Siriagus 15:d5eb13c4c1c6 151 if (!col)
Siriagus 15:d5eb13c4c1c6 152 {
Siriagus 15:d5eb13c4c1c6 153 ++it; // go to next element
Siriagus 15:d5eb13c4c1c6 154 bullet->x += bullet->vx; // update position
Siriagus 15:d5eb13c4c1c6 155 }
Siriagus 15:d5eb13c4c1c6 156 else
Siriagus 15:d5eb13c4c1c6 157 {
Siriagus 15:d5eb13c4c1c6 158 delete bullet;
Siriagus 9:da608ae65df9 159 it = bullets.erase(it); // go to next element
Siriagus 9:da608ae65df9 160 }
Siriagus 8:9ac6a428fa26 161
Siriagus 8:9ac6a428fa26 162 // TODO: Check for collisions
Siriagus 8:9ac6a428fa26 163 // TODO: Go both ways
Siriagus 8:9ac6a428fa26 164 }
Siriagus 7:678873947b29 165 }
Siriagus 7:678873947b29 166
Siriagus 7:678873947b29 167 void Game::render()
Siriagus 7:678873947b29 168 {
Siriagus 11:adb68da98262 169 // Draw map
Siriagus 11:adb68da98262 170 drawImage(map);
Siriagus 11:adb68da98262 171
Siriagus 12:8178fad5e660 172 // Draw player
Siriagus 13:7ab71c7c311b 173 drawImage(Image::Player, player.x, player.y, false, !player.facingLeft);
Siriagus 13:7ab71c7c311b 174
Siriagus 13:7ab71c7c311b 175 // Draw enemies
Siriagus 16:caf613d5b85e 176 for (std::vector<Enemy*>::iterator it = enemies.begin(); it != enemies.end(); ++it)
Siriagus 16:caf613d5b85e 177 {
Siriagus 16:caf613d5b85e 178 Enemy *enemy = *it;
Siriagus 16:caf613d5b85e 179 drawImage(Image::EnemySimple, enemy->x, enemy->y, false, !enemy->facingLeft, enemy->dead);
Siriagus 16:caf613d5b85e 180 }
Siriagus 16:caf613d5b85e 181
Siriagus 9:da608ae65df9 182 // Render bullets
Siriagus 8:9ac6a428fa26 183 for (std::vector<Point*>::iterator it = bullets.begin(); it != bullets.end(); ++it)
Siriagus 8:9ac6a428fa26 184 {
Siriagus 8:9ac6a428fa26 185 int x, y;
Siriagus 8:9ac6a428fa26 186 x = (*it)->x;
Siriagus 8:9ac6a428fa26 187 y = (*it)->y;
Siriagus 8:9ac6a428fa26 188
Siriagus 8:9ac6a428fa26 189 if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) // Boundary check
Siriagus 8:9ac6a428fa26 190 lcd->setPixel(x,y);
Siriagus 8:9ac6a428fa26 191 }
Siriagus 15:d5eb13c4c1c6 192
Siriagus 15:d5eb13c4c1c6 193 // Draw pause
Siriagus 15:d5eb13c4c1c6 194 if (paused)
Siriagus 15:d5eb13c4c1c6 195 {
Siriagus 15:d5eb13c4c1c6 196 lcd->drawRect(24, 13, 40, 13, 0); // outline
Siriagus 15:d5eb13c4c1c6 197 lcd->drawRect(25, 14, 38, 11, 2); // white fill
Siriagus 15:d5eb13c4c1c6 198 lcd->printString("Paused", 27, 2); // text
Siriagus 15:d5eb13c4c1c6 199 }
Siriagus 13:7ab71c7c311b 200 }
Siriagus 13:7ab71c7c311b 201
Siriagus 13:7ab71c7c311b 202 // Collision test between entites and map
Siriagus 13:7ab71c7c311b 203 void Game::moveWithCollisionTest(Entity* entity, const int map[HEIGHT][WIDTH])
Siriagus 13:7ab71c7c311b 204 {
Siriagus 13:7ab71c7c311b 205 int x = entity->x;
Siriagus 13:7ab71c7c311b 206 int y = entity->y;
Siriagus 13:7ab71c7c311b 207 int steps = abs(entity->vx); // how many units (pixels) the entity should move in said direction
Siriagus 13:7ab71c7c311b 208 bool collision; // true if colliding
Siriagus 13:7ab71c7c311b 209
Siriagus 13:7ab71c7c311b 210 // Check x-axis
Siriagus 13:7ab71c7c311b 211 if (entity->vx > 0) // moving right
Siriagus 13:7ab71c7c311b 212 {
Siriagus 13:7ab71c7c311b 213 int entityRight = x + entity->width - 1; // Need to check right border of entity, since it is moving right
Siriagus 13:7ab71c7c311b 214
Siriagus 13:7ab71c7c311b 215 while(steps--) // While it still have more movement left
Siriagus 13:7ab71c7c311b 216 {
Siriagus 13:7ab71c7c311b 217 collision = false;
Siriagus 16:caf613d5b85e 218
Siriagus 16:caf613d5b85e 219 // Wrapping
Siriagus 16:caf613d5b85e 220 if (entityRight+1 >= WIDTH)
Siriagus 16:caf613d5b85e 221 entityRight = -1;// wants entityRight = -1, so next check is entityRight 0*/
Siriagus 16:caf613d5b85e 222
Siriagus 13:7ab71c7c311b 223 for (int i = 0; i < entity->height; ++i) // Loop through all vertical points on the right hand side of the entity (y+i)
Siriagus 13:7ab71c7c311b 224 {
Siriagus 13:7ab71c7c311b 225 if (map[y+i][entityRight+1]) // If moving to the right leads to collision for given y+i
Siriagus 13:7ab71c7c311b 226 {
Siriagus 15:d5eb13c4c1c6 227 // Slope + allows player to climb to top of platform by going right if it hits close to top of wall.
Siriagus 15:d5eb13c4c1c6 228 if (!map[y+i-1][entityRight+1] && entity->onGround)
Siriagus 15:d5eb13c4c1c6 229 {
Siriagus 15:d5eb13c4c1c6 230 entity->vy = -1;
Siriagus 15:d5eb13c4c1c6 231 }
Siriagus 15:d5eb13c4c1c6 232 else
Siriagus 15:d5eb13c4c1c6 233 {
Siriagus 15:d5eb13c4c1c6 234 collision = true; // Then collision is true
Siriagus 15:d5eb13c4c1c6 235 break; // Skip the for loop, no need for further testing
Siriagus 15:d5eb13c4c1c6 236 }
Siriagus 15:d5eb13c4c1c6 237
Siriagus 13:7ab71c7c311b 238 }
Siriagus 13:7ab71c7c311b 239 }
Siriagus 13:7ab71c7c311b 240
Siriagus 13:7ab71c7c311b 241 if (collision) // If collision
Siriagus 13:7ab71c7c311b 242 break; // skip the while loop, entity can not move further, even though its velocity is higher
Siriagus 13:7ab71c7c311b 243 else
Siriagus 13:7ab71c7c311b 244 ++entityRight; // Move entity one px to the right
Siriagus 13:7ab71c7c311b 245 }
Siriagus 13:7ab71c7c311b 246
Siriagus 16:caf613d5b85e 247 // If wrap didn't work, make sure entity is on the correct side of the map
Siriagus 16:caf613d5b85e 248 if (entityRight < 0)
Siriagus 16:caf613d5b85e 249 entityRight = WIDTH-1;
Siriagus 16:caf613d5b85e 250
Siriagus 13:7ab71c7c311b 251 entity->x = entityRight - (entity->width - 1); // Update entity's position. Need to set upper-left pixel.
Siriagus 13:7ab71c7c311b 252 }
Siriagus 13:7ab71c7c311b 253 else // moving left
Siriagus 13:7ab71c7c311b 254 {
Siriagus 13:7ab71c7c311b 255 while(steps--) // While still movement left
Siriagus 13:7ab71c7c311b 256 {
Siriagus 13:7ab71c7c311b 257 collision = false;
Siriagus 13:7ab71c7c311b 258
Siriagus 16:caf613d5b85e 259 // Wrap around map
Siriagus 16:caf613d5b85e 260 if (x-1 < 0)
Siriagus 16:caf613d5b85e 261 x = WIDTH; // causes x-1 in the next check to be WIDTH - 1
Siriagus 13:7ab71c7c311b 262
Siriagus 16:caf613d5b85e 263 // Check for all y-positions
Siriagus 16:caf613d5b85e 264 for (int i = 0; i < entity->height; ++i)
Siriagus 13:7ab71c7c311b 265 {
Siriagus 16:caf613d5b85e 266
Siriagus 13:7ab71c7c311b 267 if (map[y+i][x-1]) // If solid block
Siriagus 13:7ab71c7c311b 268 {
Siriagus 15:d5eb13c4c1c6 269 if (!map[y+i-1][x-1] && entity->onGround) // If slope or close to top of wall (=> can climb by going left).
Siriagus 15:d5eb13c4c1c6 270 {
Siriagus 15:d5eb13c4c1c6 271 entity->vy = -1;
Siriagus 15:d5eb13c4c1c6 272 }
Siriagus 15:d5eb13c4c1c6 273 else
Siriagus 15:d5eb13c4c1c6 274 {
Siriagus 15:d5eb13c4c1c6 275 collision = true;
Siriagus 15:d5eb13c4c1c6 276 break; // Collision detected, no further testing required
Siriagus 15:d5eb13c4c1c6 277 }
Siriagus 13:7ab71c7c311b 278 }
Siriagus 13:7ab71c7c311b 279 }
Siriagus 13:7ab71c7c311b 280
Siriagus 13:7ab71c7c311b 281 if (collision)
Siriagus 13:7ab71c7c311b 282 break;
Siriagus 13:7ab71c7c311b 283 else
Siriagus 13:7ab71c7c311b 284 --x; // Move to the left if no collision is detected
Siriagus 13:7ab71c7c311b 285 }
Siriagus 13:7ab71c7c311b 286
Siriagus 16:caf613d5b85e 287 x %= WIDTH; // In case wrapping caused entity to crash with wall on other side, x should be 0 instead of WIDTH (invalid).
Siriagus 16:caf613d5b85e 288
Siriagus 16:caf613d5b85e 289 entity->x = x; // update position
Siriagus 13:7ab71c7c311b 290 }
Siriagus 13:7ab71c7c311b 291
Siriagus 13:7ab71c7c311b 292 // Check collision with map in y-direction - works the same way as the x-axis, except for other axis
Siriagus 13:7ab71c7c311b 293 x = entity->x;
Siriagus 13:7ab71c7c311b 294 y = entity->y;
Siriagus 13:7ab71c7c311b 295 steps = abs(entity->vy);
Siriagus 13:7ab71c7c311b 296
Siriagus 13:7ab71c7c311b 297 if (entity->vy > 0) // downwards
Siriagus 13:7ab71c7c311b 298 {
Siriagus 13:7ab71c7c311b 299 int entityBottom = y + entity->height - 1; // Need to check if bottom part collides
Siriagus 13:7ab71c7c311b 300 while(steps--) // Still movement left
Siriagus 13:7ab71c7c311b 301 {
Siriagus 13:7ab71c7c311b 302 collision = false;
Siriagus 16:caf613d5b85e 303
Siriagus 13:7ab71c7c311b 304 for (int i = 0; i < entity->width; ++i) // Loop through all x-position on lower part of entity
Siriagus 16:caf613d5b85e 305 {
Siriagus 16:caf613d5b85e 306 if (map[(entityBottom+1) % HEIGHT][x+i]) // If moving the entity one step down for a given (x+i)-position gives a collision
Siriagus 13:7ab71c7c311b 307 {
Siriagus 13:7ab71c7c311b 308 collision = true;
Siriagus 13:7ab71c7c311b 309 break; // No further testing required
Siriagus 13:7ab71c7c311b 310 }
Siriagus 13:7ab71c7c311b 311 }
Siriagus 13:7ab71c7c311b 312
Siriagus 13:7ab71c7c311b 313 if (collision) // If collision
Siriagus 13:7ab71c7c311b 314 {
Siriagus 13:7ab71c7c311b 315 entity->vy = 0; // Set vertical velocity to 0 (playe
Siriagus 13:7ab71c7c311b 316 entity->onGround = true; // entity has hit ground
Siriagus 13:7ab71c7c311b 317 break; // Skip the while loop as the entity can not move further downwards
Siriagus 13:7ab71c7c311b 318 }
Siriagus 13:7ab71c7c311b 319 else // Can safely move entity without collision
Siriagus 15:d5eb13c4c1c6 320 {
Siriagus 13:7ab71c7c311b 321 ++entityBottom; // Move entity one step down
Siriagus 15:d5eb13c4c1c6 322 entity->onGround = false;
Siriagus 15:d5eb13c4c1c6 323 }
Siriagus 13:7ab71c7c311b 324 }
Siriagus 13:7ab71c7c311b 325
Siriagus 16:caf613d5b85e 326 // Wrapping
Siriagus 16:caf613d5b85e 327 y = (entityBottom - (entity->height - 1));
Siriagus 16:caf613d5b85e 328 if (y >= HEIGHT) // if completely outside map
Siriagus 16:caf613d5b85e 329 y = -entity->height; // wrap to top of map
Siriagus 16:caf613d5b85e 330
Siriagus 16:caf613d5b85e 331 entity->y = y; // (entityBottom - (entity->height - 1)); // Update position when done moving, remember that entity.y refers to upper part of the entity
Siriagus 13:7ab71c7c311b 332 }
Siriagus 13:7ab71c7c311b 333 else // moving up, check collision from top
Siriagus 13:7ab71c7c311b 334 {
Siriagus 13:7ab71c7c311b 335 while(steps--) // Still movement left
Siriagus 13:7ab71c7c311b 336 {
Siriagus 13:7ab71c7c311b 337 collision = false;
Siriagus 16:caf613d5b85e 338
Siriagus 13:7ab71c7c311b 339 for (int i = 0; i < entity->width; ++i) // Check for all x-positions
Siriagus 13:7ab71c7c311b 340 {
Siriagus 16:caf613d5b85e 341 int y1 = ((y-1) + HEIGHT) % HEIGHT; // In case negative, because of wrapping
Siriagus 16:caf613d5b85e 342
Siriagus 16:caf613d5b85e 343 if (map[y1][x+i]) // If moving upwards gives collision for a given x+i
Siriagus 13:7ab71c7c311b 344 {
Siriagus 13:7ab71c7c311b 345 collision = true; // Then we have a collision
Siriagus 13:7ab71c7c311b 346 break; // No further testing needed, skip for loop
Siriagus 13:7ab71c7c311b 347 }
Siriagus 13:7ab71c7c311b 348 }
Siriagus 13:7ab71c7c311b 349
Siriagus 13:7ab71c7c311b 350 if (collision) // If collision was detected
Siriagus 13:7ab71c7c311b 351 {
Siriagus 13:7ab71c7c311b 352 entity->vy = 0; // Set vertical velocity to zero
Siriagus 13:7ab71c7c311b 353 break; // Skip while loop as entity can not move further up
Siriagus 13:7ab71c7c311b 354 }
Siriagus 13:7ab71c7c311b 355 else // If safe to move for all x-values
Siriagus 13:7ab71c7c311b 356 --y; // Move entity one step up
Siriagus 13:7ab71c7c311b 357 }
Siriagus 13:7ab71c7c311b 358
Siriagus 16:caf613d5b85e 359 // Wrapping
Siriagus 16:caf613d5b85e 360 if (y + (entity->height - 1) < 0) // completely outside map (bottom of entity over top of map)
Siriagus 16:caf613d5b85e 361 y = HEIGHT-1 - entity->height - 1; // Sets the altitude.
Siriagus 16:caf613d5b85e 362
Siriagus 13:7ab71c7c311b 363 entity->y = y; // Update vertical position of entity
Siriagus 13:7ab71c7c311b 364 }
Siriagus 15:d5eb13c4c1c6 365 }
Siriagus 15:d5eb13c4c1c6 366
Siriagus 15:d5eb13c4c1c6 367 bool Game::hitTestRect(Rectangle r1, Rectangle r2)
Siriagus 15:d5eb13c4c1c6 368 {
Siriagus 15:d5eb13c4c1c6 369 return ((r1.x + r1.width > r2.x) // r1's right edge to the right of r2's left edge
Siriagus 15:d5eb13c4c1c6 370 && (r1.x < r2.x + r2.width) // r1's left edge to the left of r2's right edge
Siriagus 15:d5eb13c4c1c6 371 && (r1.y + r2.height > r2.y) // r1's bottom lower than r2's top
Siriagus 15:d5eb13c4c1c6 372 && (r1.y < r2.y + r2.height)); // r1's top higher than r2's bottom
Siriagus 15:d5eb13c4c1c6 373
Siriagus 15:d5eb13c4c1c6 374 // Note: Right border: r1.x + r1.width - 1, but we don't need to subtract 1 as we use > instead of >=
Siriagus 15:d5eb13c4c1c6 375 }
Siriagus 15:d5eb13c4c1c6 376
Siriagus 15:d5eb13c4c1c6 377 bool Game::bulletHitMap(Rectangle &bulletColRect, const int map[HEIGHT][WIDTH])
Siriagus 15:d5eb13c4c1c6 378 {
Siriagus 15:d5eb13c4c1c6 379 for (int j = 0; j < bulletColRect.width; ++j)
Siriagus 15:d5eb13c4c1c6 380 {
Siriagus 15:d5eb13c4c1c6 381 if (map[bulletColRect.y][bulletColRect.x + j])
Siriagus 15:d5eb13c4c1c6 382 return true;
Siriagus 15:d5eb13c4c1c6 383 }
Siriagus 15:d5eb13c4c1c6 384
Siriagus 15:d5eb13c4c1c6 385 return false;
Siriagus 16:caf613d5b85e 386 }
Siriagus 16:caf613d5b85e 387
Siriagus 16:caf613d5b85e 388 void Game::moveEnemies()
Siriagus 16:caf613d5b85e 389 {
Siriagus 16:caf613d5b85e 390 for (std::vector<Enemy*>::iterator it = enemies.begin(); it != enemies.end(); ++it)
Siriagus 16:caf613d5b85e 391 {
Siriagus 16:caf613d5b85e 392 Enemy *enemy = *it;
Siriagus 16:caf613d5b85e 393
Siriagus 16:caf613d5b85e 394 // Random movement for enemies
Siriagus 16:caf613d5b85e 395 if (enemy->onGround && (rand() % 100) > 97) // 3 % chance
Siriagus 16:caf613d5b85e 396 {
Siriagus 16:caf613d5b85e 397 // jump
Siriagus 16:caf613d5b85e 398 enemy->vy = -3;
Siriagus 16:caf613d5b85e 399 enemy->onGround = false;
Siriagus 16:caf613d5b85e 400 }
Siriagus 16:caf613d5b85e 401 else if ((rand() % 100) > 98) // 2% chance
Siriagus 16:caf613d5b85e 402 {
Siriagus 16:caf613d5b85e 403 // switch direction
Siriagus 16:caf613d5b85e 404 enemy->vx *= -1;
Siriagus 16:caf613d5b85e 405 enemy->facingLeft = (enemy->vx < 0);
Siriagus 16:caf613d5b85e 406 }
Siriagus 16:caf613d5b85e 407
Siriagus 16:caf613d5b85e 408 // Gravity
Siriagus 16:caf613d5b85e 409 enemy->vy += 1;
Siriagus 16:caf613d5b85e 410
Siriagus 16:caf613d5b85e 411 if (!enemy->dead)
Siriagus 16:caf613d5b85e 412 {
Siriagus 16:caf613d5b85e 413 moveWithCollisionTest(enemy, map);
Siriagus 16:caf613d5b85e 414
Siriagus 16:caf613d5b85e 415 // Enemy AI
Siriagus 16:caf613d5b85e 416 int nextRight = enemy->getRight() + 1; // Next position of right edge if enemy moves to the right
Siriagus 16:caf613d5b85e 417 nextRight %= WIDTH; // wrapping
Siriagus 16:caf613d5b85e 418 for (int i = 0; i < enemy->height; ++i) // Check for all heighs
Siriagus 16:caf613d5b85e 419 {
Siriagus 16:caf613d5b85e 420 // Check if crashing if moving right or left. Bounds should already be limited by moveWithCollisionTest!
Siriagus 16:caf613d5b85e 421 if (map[enemy->y + i][nextRight] || map[enemy->y + i][enemy->x - 1])
Siriagus 16:caf613d5b85e 422 {
Siriagus 16:caf613d5b85e 423 enemy->vx *= -1; // move in opposite direction
Siriagus 16:caf613d5b85e 424 enemy->facingLeft = !enemy->facingLeft; // toggle direction
Siriagus 16:caf613d5b85e 425 break; // no further testing required
Siriagus 16:caf613d5b85e 426 }
Siriagus 16:caf613d5b85e 427 }
Siriagus 16:caf613d5b85e 428 }
Siriagus 16:caf613d5b85e 429 else
Siriagus 16:caf613d5b85e 430 {
Siriagus 16:caf613d5b85e 431 enemy->y += enemy->vy;
Siriagus 16:caf613d5b85e 432 enemy->x += enemy->vx;
Siriagus 16:caf613d5b85e 433 }
Siriagus 16:caf613d5b85e 434 }
Siriagus 7:678873947b29 435 }