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 09:37:51 2015 +0000
Revision:
15:d5eb13c4c1c6
Parent:
14:b4fed570abaf
Child:
16:caf613d5b85e
Enemy - bullet collison + entities can walk upwards slopes or climb walls with a height smaller than themselves.

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