Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Collision detection and keyboard input question..

This thread is locked; no one can reply to it. rss feed Print
Collision detection and keyboard input question..
Gnamra
Member #12,503
January 2011
avatar

This is my first attempt at collision detection and I'm going to use bounding box / rectangle collision detection, do I need to check with all the objects on the map? If I have 100 different platforms, boxes and other stuff do I need to check with all of them if they collide with the player? This seems really tedious and inefficient..

I also have a problem with the keyboard input / events in my game, when I press left / right arrow key then press the up arrow key to jump while holding left / right it stops moving to the left / right.

What I thought would happen, if this is the event queue: right, right, right, up, right, right, right, etc etc.

What actually happens: Right, right, right, up then it stops.

Sorry if my explaining is a bit confusing. Here's some code.

Player.cpp Move function.

#SelectExpand
1void Player::Move(ALLEGRO_EVENT* events) 2{ 3 4 if(events->type = ALLEGRO_EVENT_KEY_DOWN) 5 { 6 switch(events->keyboard.keycode) 7 { 8 case ALLEGRO_KEY_DOWN: 9 break; 10 case ALLEGRO_KEY_UP: 11 Player::Jump(); 12 break; 13 case ALLEGRO_KEY_RIGHT: 14 if(Player::vel_X <=10)Player::vel_X += 1; 15 break; 16 case ALLEGRO_KEY_LEFT: 17 if(Player::vel_X >=-10)Player::vel_X -= 1; 18 break; 19 }; 20 } 21}

The jumping function only sets the vel_Y to -20 and sets jumping = true

My game loop.

#SelectExpand
1while(!done) 2{ 3 al_clear_to_color(al_map_rgb(0,0,0)); 4 ALLEGRO_EVENT events; 5 if(al_wait_for_event_timed(event_queue, &events, 0.0001)) player.Move(&events); 6 game.update(&player); 7 game.draw(&player); 8 al_rest(0.030); 9}

my Game.update

#SelectExpand
1 void Game::update(Player *p) 2{ 3 p->setX(p->getfX() + p->getfVel_X()); 4 p->setY(p->getfY() + p->getfVel_Y()); 5 if(p->getfVel_X() > 0.1 && !p->jumping) p->setVel_X(p->getfVel_X() - 0.5); 6 if(p->getfVel_X() < -0.1 && !p->jumping) p->setVel_X(p->getfVel_X() + 0.5); 7 if(p->getfY() < 400) p->setVel_Y(p->getfVel_Y() + 5); 8 if(p->getfY() == 400.0) 9 { 10 p->setVel_Y(0); 11 p->jumping = false; 12 } 13 if(p->getfY() > 400) 14 { 15 p->setY(400); 16 p->jumping = false; 17 } 18 19};

Right now it's only a "P" being drawn on a black background, I'm trying to perfect character movement before I add platforms, images, sound etc

oh and, whats the difference between Player::vel_X and just typing vel_X in player.cpp?
Edit: nevermind, there is no difference between Player::vel_X and typing vel_X, it's a matter of style for anyone else who might be wondering the same thing :)

Stas B.
Member #9,615
March 2008

Gnamra said:

This is my first attempt at collision detection and I'm going to use bounding box / rectangle collision detection, do I need to check with all the objects on the map? If I have 100 different platforms, boxes and other stuff do I need to check with all of them if they collide with the player? This seems really tedious and inefficient..

If this is your first attempt at collision detection, I'd say you have a long way to go before you get to the point where optimizing bounding box checks becomes a real concern. Concentrate on getting something that feels like a game done, then optimize, if needed. It's easy to get carried away trying to figure out how to speed things up with space partitioning.

Well, since I've already mentioned it, the general idea is to divide the space into simple, non-intersecting regions, figure a fast algorithm to find out which region an object is inside, then check for collisions only between objects that are in the same region. The most common method for a 2D game is probably a simple grid of squares. You can try quad trees if you want to get fancy, but it's almost always overkill.

Gnamra
Member #12,503
January 2011
avatar

Stas B. said:

Well, since I've already mentioned it, the general idea is to divide the space into simple, non-intersecting regions, figure a fast algorithm to find out which region an object is inside, then check for collisions only between objects that are in the same region. The most common method for a 2D game is probably a simple grid of squares. You can try quad trees if you want to get fancy, but it's almost always overkill.

I see, so checking for collision with all the objects in a level isn't something that will slow down the game? Even if there are hundreds or perhaps thousands of objects?

Thanks :) Only need to workout the keyboard problem now.

Cassio Renan
Member #14,189
April 2012
avatar

A fast and easy way to improve collision detection(if it's slow) is to check for bounding circles first, and then for bounding boxes. That way your algorithm will only check for boxes if the sprites are close enough to do so.
Comparing the squared distances of the sprites centre's is the fastest way to do it.

in pseudo-code(actually, I guess this works fine in C):

#SelectExpand
1bool check_collision(sprite a,sprite b) 2{ 3 // a's centre x 4 a.cx = a.x + a.w/2 5 // a's centre y 6 a.cy = a.y + a.h/2 7 8 // a's radium. you should get it at creation of the sprite, 9 // not at collision time, since you only need to get it once. 10 // it's simply the distance from the centre to the up-left 11 // vertice of the sprite. 12 a.r = distance(a.x, a.y, a.cx, a.cy) 13 14 // Same for "b" 15 b.cx = b.x + b.w/2 // b's centre x 16 b.cy = b.y + b.h/2 // b's centre y 17 b.r = distance(b.x, b.y, b.cx, b.cy) // b's radium 18 19 // forget the sqrt, it's slow. The squared distance will already work. 20 distance = pow(a.cx - b.cx, 2) + pow(a.cy - b.cy, 2) 21 22 if(distance <= pow(a.r + b.r)) 23 return check_bounding_boxes(a, b) 24 else 25 return false; 26}

Could anyone confirm this is a good practice? I sometimes do it this way and it works most time.(guess I turned my answer to a question, ::))

David Sopala
Member #5,056
September 2004

You might be better off doing something like:

I think an angled line could cut corners if you have oddly shaped sprites.

max(sprite->w,sprite->h);

Stas B.
Member #9,615
March 2008

Gnamra said:

I see, so checking for collision with all the objects in a level isn't something that will slow down the game? Even if there are hundreds or perhaps thousands of objects?

Hundreds? Nope. Thousands? Probably not enough to slow down a modern PC, but if you manage to get into that range, it will probably become worthwhile to free up some CPU cycles for the other tasks needed to handle thousands of objects. Note that when you say thousands of objects, I assume that each one of them may collide with any other. That's not usually the case. Either way, my point is that you shouldn't worry about this yet, whether it will become a problem in the future or not. Focus on the fun part of making an actual game. Optimizations are a huge time sink and it's quite demotivating when you realize you've spent two weeks trying to implement quad trees but don't have anything that even remotely resembles a game yet.

A fast and easy way to improve collision detection(if it's slow) is to check for bounding circles first, and then for bounding boxes. That way your algorithm will only check for boxes if the sprites are close enough to do so.
Comparing the squared distances of the sprites centre's is the fastest way to do it.

Depends on the type of bounding boxes. If they're axis-aligned, it's a waste of time, since a method to check wether they intersect should look roughly like this:

    if(min.x > other.max.x || min.y > other.max.y || other.min.x > max.x || other.min.y > max.y) return false;
    else return true;

If they're allowed to rotate, well, that's another story, but I think the OP doesn't really care for rotating bounding boxes yet.

Gnamra
Member #12,503
January 2011
avatar

Thanks for all the help, I think rotating bounding boxes and quad trees are beyond me right now, so I'll save that for later!

Uhm.. about the problem I have with key events. Do all keyboards stop repeating the keystroke when another key was pressed? I'm currently trying to figure out how to keep sending keystrokes of the last button pressed excluding the jump button. If someone has a fix for my problem then please post :)

Cassio Renan
Member #14,189
April 2012
avatar

well,
doesn't really solve your problem, but I have a question:
why

#SelectExpand
8if(p->getfY() == 400.0) 9 { 10 p->setVel_Y(0); 11 p->jumping = false; 12 } 13 if(p->getfY() > 400) 14 { 15 p->setY(400); 16 p->jumping = false; 17 }

and not

#SelectExpand
8if(p->getfY() >= 400.0 &&p->jumping) 9 { 10 p->setVel_Y(0); 11 p->setY(400); 12 p->jumping = false; 13 }

?

EDIT: Added "p->jumping" into the condition, so it only happens during jump.

Gnamra
Member #12,503
January 2011
avatar

Because I'm stupid sometimes :P edited the code to match yours.

I have a bad habit of creating more if statements than needed, at that point I was trying to figure out why the velocity would sometimes continue to increase even though it hit 400. Then I realized it was because it never actually hit 400 but went past it so I made another if statement for when that happens.

Also, I found a workaround for the keyboard / key event problem I had.

For anyone who stumbles into the same problem, here's how I fixed it

player.cpp

#SelectExpand
1 void Player::Move(ALLEGRO_EVENT* events) 2{ 3 if(keyDown == true){ 4 switch(lastEvent.keyboard.keycode) 5 { 6 case ALLEGRO_KEY_RIGHT: 7 keyDown = true; 8 lastEvent.keyboard.keycode = ALLEGRO_KEY_RIGHT; 9 if(Player::vel_X <= 10)Player::vel_X += 1; 10 break; 11 12 case ALLEGRO_KEY_LEFT: 13 keyDown = true; 14 lastEvent.keyboard.keycode = ALLEGRO_KEY_LEFT; 15 if(Player::vel_X >= -10)Player::vel_X -= 1; 16 break; 17 }; 18 } 19 20 if(events->type == ALLEGRO_EVENT_KEY_DOWN && events->keyboard.keycode != ALLEGRO_KEY_UP || keyDown == true) 21 { 22 switch(events->keyboard.keycode) 23 { 24 case ALLEGRO_KEY_RIGHT: 25 keyDown = true; 26 lastEvent.keyboard.keycode = ALLEGRO_KEY_RIGHT; 27 if(Player::vel_X <= 10)Player::vel_X += 1; 28 break; 29 30 case ALLEGRO_KEY_LEFT: 31 keyDown = true; 32 lastEvent.keyboard.keycode = ALLEGRO_KEY_LEFT; 33 if(Player::vel_X >= -10)Player::vel_X -= 1; 34 break; 35 }; 36 } 37 if(events->type == ALLEGRO_EVENT_KEY_UP && events->keyboard.keycode != ALLEGRO_KEY_UP) keyDown = false; 38 if(events->type == ALLEGRO_EVENT_KEY_DOWN && events->keyboard.keycode == ALLEGRO_KEY_UP) Player::Jump(); 39}

What I did was I added
bool keyDown;
ALLEGRO_EVENT lastEvent;
in the Player.h

And stored the last keyboard keycode in lastEvent.

There is most likely a better way to do it :P

Go to: