Only 1 arrow at a time can collide
DJLad16

So I decided to put my arrows into a class array discarding the idea of limited arrows. But when I did that, only 1 arrow would collide with the enemy at once. I.e if I shot several arrows with none of them hitting an enemy, then I go to hit an enemy, it would go straight through without colliding.
When I run the program I get something saying "warnig C4175 : 'collideArrow' : not all control paths return a value". Has this got something to do with it?
(I've removed some code)

#SelectExpand
1void initPlayer(Player &player); 2 3void initEnemy(Enemy &enemy, int x, int y, int speedR, int speedL, int speed); 4void enemyAnimtaion(Enemy &enemy, ALLEGRO_BITMAP *image); 5void drawEnemy(Enemy &enemy, ALLEGRO_BITMAP *image); 6void updateEnemy(Enemy &enemy, int posX, int posX2); 7bool collideArrow(Enemy &enemy, Arrow arrow[], int size); 8 9 10void initArrow(Arrow arrow[], int size); 11void drawArrow(Arrow arrow[], int size, ALLEGRO_BITMAP *image, ALLEGRO_BITMAP *image2, Player &player); 12void fireArrow(Arrow arrow[], int size, Player &player, ALLEGRO_SAMPLE *bowShot); 13void updateArrow(Arrow arrow[], int size, Player &player); 14 15 16bool collision(Player &player, int ex, int ey, int eWidth, int eHeight, int pWidth, int pHeight); 17 18void handleArrowCollision(Enemy &enemy, Arrow arrow[], Player &player, int size); 19 20void cameraUpdate(float *cameraPosition,Player &player, float width, float height); 21 22//Global Variables 23 24 25int main() 26{ 27 28 29 30 31 al_reserve_samples(5); //Respresents how many audio clips I have in the game 32 al_set_display_icon(display, icon); 33 34 al_set_sample_instance_playmode(songInstance, ALLEGRO_PLAYMODE_LOOP); 35 al_attach_sample_instance_to_mixer(songInstance, al_get_default_mixer()); 36 al_set_window_title(display, "Australian Outback"); 37 al_register_event_source(event_queue, al_get_keyboard_event_source()); 38 al_register_event_source(event_queue, al_get_timer_event_source(timer)); 39 al_register_event_source(event_queue, al_get_timer_event_source(frameTimer)); 40 al_register_event_source(event_queue, al_get_timer_event_source(enemyTimer)); 41 al_register_event_source(event_queue, al_get_display_event_source(display)); 42 43 al_play_sample_instance(songInstance); 44 45 al_start_timer(timer); 46 al_start_timer(frameTimer); 47 al_start_timer(enemyTimer); 48 while(!done) 49 { 50 ALLEGRO_EVENT event; 51 al_wait_for_event(event_queue, &event); 52 al_get_keyboard_state(&keystate); 53 54 if(al_key_down(&keystate, ALLEGRO_KEY_ESCAPE)) 55 { 56 done = true; 57 } 58 else if(event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) 59 { 60 done = true; 61 } 62 63 64 else if(event.type == ALLEGRO_EVENT_TIMER) 65 { 66 if(event.timer.source == timer) 67 { 68 active = true; 69 updateArrow(arrow, NUM_ARROW ,player); 70 updateEnemy(enemy, 238, 78); 71 updateEnemy(enemy2, 1236, 1167); 72 updateEnemy(enemy3, 660, 378); 73 updateEnemy(enemy4, 660, 378); 74 updateEnemy(enemy5, 2163, 1599); 75 updateEnemy(enemy6, 2163, 1599); 76 updateEnemy(enemy7, 2163, 1599); 77 updateEnemy(enemy8, 2163, 1599); 78 updateEnemy(enemy9, 2163, 1599); 79 updateEnemy(enemy10, 1506, 1306); 80 frames++; 81 if(al_current_time() - gameTime >= 1) 82 { 83 gameTime = al_current_time(); 84 gameFPS = frames; 85 frames = 0; 86 } 87 88 if(al_key_down(&keystate, ALLEGRO_KEY_SPACE)) 89 { 90 fireArrow(arrow, NUM_ARROW, player, bowShot); 91 } 92 else 93 fired = false; 94 if(al_key_down(&keystate, ALLEGRO_KEY_D)) 95 { 96 velX = player.speed; 97 player.dir = RIGHT; 98 99 } 100 else if(al_key_down(&keystate, ALLEGRO_KEY_A)) 101 { 102 velX = -player.speed; 103 player.dir = LEFT; 104 } 105 else 106 { 107 velX = 0; 108 active = false; 109 } 110 if(al_key_down(&keystate, ALLEGRO_KEY_W) && jump) 111 { 112 velY = -jumpSpeed; 113 jump = false; 114 } 115 116 } 117 if(active) 118 { 119 sourceX += al_get_bitmap_width(character) / 3; 120 } 121 else 122 { 123 sourceX = 32; 124 } 125 if(sourceX >= al_get_bitmap_width(character)) 126 { 127 sourceX = 0; 128 } 129 sourceY = player.dir; 130 131 132 133 if(!jump) 134 velY += gravity; 135 else 136 velY = 0; 137 player.x += velX; 138 player.y += velY; 139 140 jump = (player.y >= groundHeight); 141 142 if(jump) 143 { 144 player.y = groundHeight; 145 } draw = true; 146 147 if(event.timer.source == enemyTimer) 148 { 149 enemyAnimtaion(enemy, enemyImg2); 150 enemyAnimtaion(enemy2, enemyImg2); 151 enemyAnimtaion(enemy3, enemyImg2); 152 enemyAnimtaion(enemy4, enemyImg2); 153 enemyAnimtaion(enemy5, enemyImg2); 154 enemyAnimtaion(enemy6, enemyImg2); 155 enemyAnimtaion(enemy7, enemyImg2); 156 enemyAnimtaion(enemy8, enemyImg2); 157 enemyAnimtaion(enemy9, enemyImg2); 158 enemyAnimtaion(enemy10, enemyImg2); 159 } 160 161 162 163 cameraUpdate(cameraPosition, player, 32, 32); 164 al_identity_transform(&camera); 165 al_translate_transform(&camera, -cameraPosition[0], -cameraPosition[1]); 166 al_use_transform(&camera); 167 168 } 169 170 171 if(collideArrow(enemy, arrow, NUM_ARROW))//Arrow Collision with enemy 172 { 173 handleArrowCollision(enemy, arrow, player, NUM_ARROW); 174 } 175 if(collideArrow(enemy2, arrow, NUM_ARROW))//Arrow Collision with enemy 176 { 177 handleArrowCollision(enemy2, arrow, player, NUM_ARROW); 178 } 179 if(collideArrow(enemy3, arrow, NUM_ARROW))//Arrow Collision with enemy 180 { 181 handleArrowCollision(enemy3, arrow, player, NUM_ARROW); 182 } 183 if(collideArrow(enemy4, arrow, NUM_ARROW))//Arrow Collision with enemy 184 { 185 handleArrowCollision(enemy4, arrow, player, NUM_ARROW); 186 } 187 if(collideArrow(enemy5, arrow, NUM_ARROW))//Arrow Collision with enemy 188 { 189 handleArrowCollision(enemy5, arrow, player, NUM_ARROW); 190 } 191 if(collideArrow(enemy6, arrow, NUM_ARROW))//Arrow Collision with enemy 192 { 193 handleArrowCollision(enemy6, arrow, player, NUM_ARROW); 194 } 195 if(collideArrow(enemy7, arrow, NUM_ARROW))//Arrow Collision with enemy 196 { 197 handleArrowCollision(enemy7, arrow, player, NUM_ARROW); 198 } 199 if(collideArrow(enemy8, arrow, NUM_ARROW))//Arrow Collision with enemy 200 { 201 handleArrowCollision(enemy8, arrow, player, NUM_ARROW); 202 } 203 if(collideArrow(enemy9, arrow, NUM_ARROW))//Arrow Collision with enemy 204 { 205 handleArrowCollision(enemy9, arrow, player, NUM_ARROW); 206 } 207 if(collideArrow(enemy10, arrow, NUM_ARROW))//Arrow Collision with enemy 208 { 209 handleArrowCollision(enemy10, arrow, player, NUM_ARROW); 210 } 211 if(collision(player, 1480, 295, 692, 305, 32, 32))//Mountain collision 212 { 213 player.x -= player.speed; 214 } 215 if(collision(player, 1505, 300, 580, 300, 32, 32))//Mountain collision 2 216 { 217 player.x = 10; 218 player.y = 535; 219 --player.lives; 220 } 221 if(collision(player, 2135, 535, 530, 50, 32, 32))//Quicksand colllision 222 { 223 player.x = 10; 224 player.y = 535; 225 --player.lives; 226 } 227 228 if(draw && al_is_event_queue_empty(event_queue)) 229 { 230 draw = false; 231 232 al_draw_bitmap(background2, 0, 0, 0); 233 al_draw_bitmap_region(character, sourceX, sourceY * al_get_bitmap_height(character) / 2, 32, 32, player.x, player.y, 0); 234 235 al_identity_transform(&identity); 236 al_use_transform(&identity); 237 al_draw_textf(font, al_map_rgb(252, 209, 22), 10, 10, 0, "Coins: %i", coinCount); 238 al_draw_textf(font, al_map_rgb(252, 209, 22), 110, 10, 0, "Lives: %i", player.lives); 239 al_draw_textf(font, al_map_rgb(252, 209, 22), 10, 45, 0, "FPS: %i", gameFPS); 240 al_flip_display(); 241 al_clear_to_color(al_map_rgb(255, 255, 255)); 242 243 } 244 } 245 246} 247void initPlayer(Player &player) 248{ 249 player.dir = LEFT; 250 player.x = 10; 251 player.y = 535; 252 player.lives = 3; 253 player.speed = 5; 254 player.score = 0; 255 player.dir = RIGHT; 256} 257 258void initEnemy(Enemy &enemy, int x, int y, int speedR, int speedL, int speed) 259{ 260 enemy.live = true; 261 enemy.speedR = speedR; 262 enemy.speedL = speedL; 263 enemy.speed = speed; 264 enemy.x = x; 265 enemy.y = y; 266 enemy.dir = eLEFT; 267 enemy.eSourceX = 32; 268 enemy.eSourceY = 0; 269 enemy.acitve = true; 270 enemy.width = 29; 271 enemy.height = 29; 272} 273void drawEnemy(Enemy &enemy, ALLEGRO_BITMAP *image) 274{ 275 if(enemy.live) 276 { 277 al_draw_bitmap_region(image, enemy.eSourceX, enemy.eSourceY * al_get_bitmap_height(image) / 2, 32, 32, enemy.x, enemy.y, 0); 278 } 279} 280void enemyAnimtaion(Enemy &enemy, ALLEGRO_BITMAP *image) 281{ 282 if(enemy.acitve) 283 { 284 enemy.eSourceX += al_get_bitmap_width(image) / 3; 285 } 286 else 287 { 288 enemy.eSourceX = 0; 289 } 290 if(enemy.eSourceX >= al_get_bitmap_width(image)) 291 { 292 enemy.eSourceX = 0; 293 } 294 enemy.eSourceY = enemy.dir; 295} 296void updateEnemy(Enemy &enemy, int posX, int posX2) 297{ 298 if(enemy.live) 299 { 300 enemy.x += enemy.speed; 301 if(enemy.x >= posX) 302 { 303 enemy.dir = eLEFT; 304 enemy.x = posX; 305 enemy.speed = enemy.speedL; 306 } 307 if(enemy.x <= posX2) 308 { 309 enemy.dir = eRIGHT; 310 enemy.x = posX2; 311 enemy.speed = enemy.speedR; 312 } 313 } 314} 315bool collideArrow(Enemy &enemy, Arrow arrow[], int size) 316{ 317 for(int i = 0; i < size; i++) 318 { 319 if((arrow[i].x > enemy.x + enemy.width) || (arrow[i].y > enemy.y + enemy.height) || 320 (enemy.x > arrow[i].x + arrow[i].height) || (enemy.y > arrow[i].y + arrow[i].height)) 321 { 322 return false; 323 } 324 return true; 325 } 326 327} 328 329 330void initArrow(Arrow arrow[], int size) 331{ 332 for(int i = 0; i < size; i++) 333 { 334 arrow[i].speed = 5.0; 335 arrow[i].live = false; 336 arrow[i].width = 16; 337 arrow[i].height = 5; 338 arrow[i].fired = false; 339 arrow[i].aDir = aRIGHT; 340 } 341 342} 343void drawArrow(Arrow arrow[], int size, ALLEGRO_BITMAP *image, ALLEGRO_BITMAP *image2, Player &player) 344{ 345 for(int i = 0; i < size; i++) 346 { 347 if(arrow[i].live && arrow[i].aDir == aRIGHT && arrow[i].fired) 348 { 349 al_draw_bitmap(image, arrow[i].x, arrow[i].y, 0); 350 } 351 if(arrow[i].live && arrow[i].aDir == aLEFT && arrow[i].fired) 352 { 353 al_draw_bitmap(image2, arrow[i].x, arrow[i].y, 0); 354 } 355 } 356} 357void fireArrow(Arrow arrow[], int size, Player &player, ALLEGRO_SAMPLE *bowShot) 358{ 359 for(int i = 0; i < size; i++) 360 { 361 if(!arrow[i].live && !fired) 362 { 363 --arrowCount; 364 arrow[i].live = true; 365 arrow[i].x = player.x + 17; 366 arrow[i].y = player.y + 22; 367 arrow[i].fired = true; 368 fired = true; 369 arrow[i].aDir = player.dir; 370 al_play_sample(bowShot, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, 0); 371 } 372 } 373} 374void updateArrow(Arrow arrow[], int size, Player &player) 375{ 376 for(int i = 0; i < size; i++) 377 { 378 if(arrow[i].live && arrow[i].fired) 379 { 380 if(arrow[i].aDir == aRIGHT) 381 { 382 arrow[i].x += arrow[i].speed; 383 if(arrow[i].x >= 2990) 384 { 385 arrow[i].live = false; 386 } 387 } 388 if(arrow[i].aDir == aLEFT) 389 { 390 arrow[i].x -= arrow[i].speed; 391 if(arrow[i].x <= 5) 392 { 393 arrow[i].live = false; 394 } 395 } 396 } 397 } 398} 399 400 401bool collision(Player &player, int ex, int ey, int eWidth, int eHeight, int pWidth, int pHeight) 402{ 403 if((player.x > ex + eWidth - 10) || (player.y > ey + eHeight - 10) || 404 (ex > player.x + pWidth - 10) || (ey > player.y + pHeight - 10)) 405 { 406 return false; 407 } 408 return true; 409} 410void handlePlatCollision(Player &player, int ex, int ey, int ewidth, int eheight, int pwidth, int pheight, int &groundheight, float vely) 411{ 412 if(player.y > ey) 413 { 414 player.y += vely; 415 groundheight = ey; 416 player.y = groundheight; 417 } 418} 419void handleArrowCollision(Enemy &enemy, Arrow arrow[], Player &player, int size) 420{ 421 for(int i = 0; i < size; i++) 422 { 423 enemy.live = false; 424 enemy.x = -500; 425 enemy.y = -500; 426 arrow[i].live = false; 427 ++player.score; 428 } 429} 430 431}

Cassio Renan

Look at your collideArrow function:

bool collideArrow(Enemy &enemy, Arrow arrow[], int size)
{
  for(int i = 0; i < size; i++)
  {
    if((arrow[i].x > enemy.x + enemy.width) || (arrow[i].y > enemy.y + enemy.height) ||
    (enemy.x > arrow[i].x + arrow[i].height) || (enemy.y > arrow[i].y + arrow[i].height))
    {
      return false;
    }
    return true;
  }
  
}

Let's pretend we are running it.
At the first loop, i == 0. At this loop, your code will test:

    if((arrow[0].x > enemy.x + enemy.width) || (arrow[0].y > enemy.y + enemy.height) ||
    (enemy.x > arrow[0].x + arrow[0].height) || (enemy.y > arrow[0].y + arrow[i].height))
    {
      return false;
    }
    return true;

If your first arrow is colliding, it will return true. If it is not, it will return false. There will never be a loop for i==1, or i >= 1, for that matter.

DJLad16

Is there anyway to fix this because I cannot think of any solutions?

adamk kromm

I would suggest you change your logic around a bit.

Currently you are checking to see if any of the arrow coordinates are outside of the enemy bounds.

if((arrow[i].x > enemy.x + enemy.width) || (arrow[i].y > enemy.y + enemy.height) ||
    (enemy.x > arrow[i].x + arrow[i].height) || (enemy.y > arrow[i].y + arrow[i].height))

Try checking to see if all the coordinates are within the enemy bounds instead. If they all are then return true, otherwise don't do anything. If you reach the end of the function then return false.

Fishcake

As Casio said, the function returns after the collision check with the first arrow, regardless of whether there is a collision or not. What you really want is to have the function return after detecting a collision with an arrow or none of the arrows. This can be achieved with some modifications to your current code:

#SelectExpand
1bool collideArrow(Enemy &enemy, Arrow arrow[], int size) 2{ 3 for(int i = 0; i < size; i++) 4 { 5 if((arrow[i].x > enemy.x + enemy.width) || (arrow[i].y > enemy.y + enemy.height) || 6 (enemy.x > arrow[i].x + arrow[i].height) || (enemy.y > arrow[i].y + arrow[i].height)) 7 { 8 // do nothing, don't return! 9 } 10 else 11 { 12 // Immediately return after detecting a collision 13 return true; 14 } 15 } 16 17 // You'll only reach here if none of the arrows are colliding 18 // with your enemy. 19 return false; 20}

However, IMO this is not a properly structured code, because having multiple return statements in a function is generally bad! (in regard to readability, especially when your function has a complex structure) And as adam said, you should really check if the arrow is within your enemy's bound instead, and not the otherwise. Something like this (pseudo code):

bool isColliding = false;
for (int i = 0; i < arrowCount; i++) {
   if (arrowIsCollidingWithEnemy) {
      isColliding = true;
      break;
   }
}

return isColliding;

Schyfis

I would suggest you change your logic around a bit.

Currently you are checking to see if any of the arrow coordinates are outside of the enemy bounds.

There's nothing wrong with this- checking if two bounding boxes overlap achieves the same result as checking if they do not overlap, just inverted.

Edit: misread some code.
I suggest at the very least that you rearrange the order of some things. For consistency, always start the condition with either arrow or enemy. It makes it more readable.

if((arrow[i].x > enemy.x + enemy.width) || (arrow[i].y > enemy.y + enemy.height) ||
    (arrow[i].x + arrow[i].height < enemy.x) || (arrow[i].y + arrow[i].height < enemy.y))

pkrcel
Schyfis said:

There's nothing wrong with this- checking if two bounding boxes overlap achieves the same result as checking if they do not overlap, just inverted.

I noticed this is recurrent in the forum these days ;D ...seems to generate a fair bit of confusion.

BTW, while I generally agree that "spamming" return statements in a function is BAD, I think that in a yes/no scenario is indeed BETTER to have a separate return statement with explicit true/false value passed in....'cause it's more readable.

Thread #612208. Printed from Allegro.cc