Collision Detection for surround walls. Please help!

Started by
1 comment, last by Krohm 12 years, 4 months ago
Hi all, can someone please teach me how do I detect collision of surrounding walls correctly....Or lead me to a good tutorial for it..

This is my first time coding a game. I am trying to code a bomberman clone, but I am having problems with handling collisions between bomberman and walls.
I was just following my intuition and it is just really hard to figure out by myself. I have debugged for the whole night already...

Here's my problem,
Even though my bomberman cannot move move diagonally,
but this is allowed,

OOWW
BBWW
BBOO
OOOO

BBBB: A Bomberman, WWWW: A Wall block O: free space

Currently, my bomberman detects the box right beside him really well. However, it fails when handling walls that are diagonal to him.

I use a bounding-box collision detection method and here is part of my code.



#define COLLISION_LEFT 1 // 0001
#define COLLISION_RIGHT 2 // 0010
#define COLLISION_UP 4 // 0100
#define COLLISION_DOWN 8 // 1000

.....
/*
* Display loop
*/
void display(void) {
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glLineWidth(1.3);
glColor3f (1.0, 1.0, 1.0);


// Draw the Map
for (int i=0; i<num_grids_x; i++) {
for (int j=0; j<num_grids_y; j++) {
blocks[j]->draw();
}
}

// Collision Detection
double x = player->getLocationX();
double y = player->getLocationY();

int x_right = roundNum(x+GRID_SIZE)/GRID_SIZE;
int x_mid = roundNum(x)/GRID_SIZE;
int x_left = roundNum(x-GRID_SIZE)/GRID_SIZE;

int y_top = roundNum(y+GRID_SIZE)/GRID_SIZE;
int y_mid = roundNum(y)/GRID_SIZE;
int y_bot = roundNum(y-GRID_SIZE)/GRID_SIZE;


// boundary conditions
if (y_top > 19) y_top = 19;
if (y_bot < 0) y_bot = 0;
if (x_right > 19) x_right = 19;
if (x_left < 0) x_left = 0;

if (y_mid > 19) y_mid = 19;
if (y_mid < 0) y_mid = 0;
if (x_mid > 19) x_mid = 19;
if (x_mid < 0) x_mid = 0;

// Check top left block
if (blocks[y_top][x_left]->getGrid() != SPACE) {
if (player->collideWith(blocks[y_top][x_left])) {
collision |= 5;
}
}

// Check top right block
if (blocks[y_top][x_right]->getGrid() != SPACE) {
if (player->collideWith(blocks[y_top][x_right])) {
collision |= 6;
}
}


// Check bottom left block
if (blocks[y_bot][x_left]->getGrid() != SPACE) {
if (player->collideWith(blocks[y_bot][x_left])) {
collision |= 9;
}
}

// Check bottom right block
if (blocks[y_bot][x_right]->getGrid() != SPACE) {
if (player->collideWith(blocks[y_bot][x_right])) {
collision |= 10;
}
}

// Check left block
if (blocks[y_mid][x_left]->getGrid() != SPACE) {
if (player->collideWith(blocks[y_mid][x_left])) {
collision |= 1;
}
} else {
collision &= 14;
}
// Check right block
if (blocks[y_mid][x_right]->getGrid() != SPACE) {
if (player->collideWith(blocks[y_mid][x_right])) {
collision |= 2;
}
} else {
collision &= 13;
}

// Check top block
if (blocks[y_top][x_mid]->getGrid() != SPACE) {
if (player->collideWith(blocks[y_top][x_mid])) {
collision |= 4;
}
} else {
collision &= 11;
}

// Check bottom block
if (blocks[y_bot][x_mid]->getGrid() != SPACE) {
if (player->collideWith(blocks[y_bot][x_mid])) {
collision |= 8;
}
} else {
collision &= 7;
}

// if collision was detected, forbid the movement in that direction
if (collision) {
switch (player_dir) {
case 1: if ((collision&1) != COLLISION_LEFT) player->updateLocationX(-0.1); break;
case 2: if ((collision&2) != COLLISION_RIGHT) player->updateLocationX(0.1); break;
case 4: if ((collision&4) != COLLISION_UP) player->updateLocationY(0.1); break;
case 8: if ((collision&8) != COLLISION_DOWN) player->updateLocationY(-0.1); break;
}
}
else {

switch (player_dir) {
case 1: player->updateLocationX(-0.1); break;
case 2: player->updateLocationX(0.1); break;
case 4: player->updateLocationY(0.1); break;
case 8: player->updateLocationY(-0.1); break;
}
}
player->draw();
glFlush();
glutSwapBuffers();

}


void processSpecialKeys(int key, int x, int y) {
switch(key) {
case GLUT_KEY_LEFT: player_dir = 1; break;
case GLUT_KEY_RIGHT: player_dir = 2; break;
case GLUT_KEY_UP: player_dir = 4; break;
case GLUT_KEY_DOWN: player_dir = 8; break;

}
}

void processSpecialKeysUp(int key, int x, int y) {
switch(key) {
case GLUT_KEY_LEFT: player_dir &= 14; break;
case GLUT_KEY_RIGHT: player_dir &= 13; break;
case GLUT_KEY_UP: player_dir &= 11; break;
case GLUT_KEY_DOWN: player_dir &= 7; break;

}
}





bool Object::collideWith(Object *rhs) {
return Object::collideWith(rhs->x, rhs->y);
}

// bounding-box collision detection
bool Object::collideWith(double x2, double y2) {
double x1 = this->x;
double y1 = this->y;

if(x1 + GRID_SIZE > x2)
{
if(x1 < x2 + GRID_SIZE)
{
if(y1 + GRID_SIZE > y2)
{
if(y1 < y2 + GRID_SIZE)
{
return true;
}
}
}
}
return false;
}

void Object::updateLocationX(double x_diff) { x += x_diff; }

void Object::updateLocationY(double y_diff) { y += y_diff; }


Sorry, my code is really messy. It's just for reference if anyone is interested...

I think the problem with my attempt is that because I hand the checks of the diagonal blocks first.
I've also tried something like

//check left
//check top left
//check bottom left
//if any collides then turn left collision on : collision |= 1
//if no collides then turn left collision off : collision &= 14

//and do it for the other three directions

However, with that approach, my bomberman cant even move properly.


I am wondering if there is a convention of coding these type of collision and if there is any good article about it can you kindly lead me to it?
Thanks!
Advertisement
The approach I always take for tilebased collision detection is as follows:


for every tile which is near to bomberman
if( bombermans x position is less than the tile x position + width / 2 and bombermans x position is greater than the tile x position - width / 2)
{
if( bombermans y position is less than the tile y position + width / 2 and bombermans y position is greater than the tile y position - width / 2)
{
return true; //a collision has happened
}
}

return false;


Then call that every time a direction key is pressed to allow or deny bomberman's movement.

but I really am a novice gamedev, so there are probably better ways!

Good luck!
Lolliver.
Why does it not collide?
Let's see. I don't know where you declare [font="Courier New"]collision[/font], but I'd take for granted it starts being 0. Let's say you hit top-left so [font="Courier New"]collision |= 5[/font] (in this context that is [font="Courier New"]collision = 5[/font]).
Now, [font="Courier New"]collision [/font]is true so the [font="Courier New"]switch [/font]fires its comparison... but this will always fail as you have used bitmasks. It can only match pure directions (up, down, left, right) as those are single-bit masks, furthermore, your logic actually clears diagonals (see below).
In short, you seem to have not understood what [font="Courier New"]switch [/font]is supposed to do.

Suppose we hit top-left (only) --> [font="Courier New"]collision == 5[/font]. 5dec == 0x05 == 0101
Then you test left, right top, bottom. All those tests fail because of assumption.
[source]
0101 &
1110 & //14
1101 & //13
1011 & // 11
0111 --> 0000 // 7

Final result: always 0
[/source]


Anyway.Perhaps it's just me but I think that's fairly backwards.
Basically what you're doing is to see if {box can move 12346789}.
I'd rather do something else. Start from the current position {5} and add the movement vector assuming it succeeds. You obtain a new bounding box at {5+M}. Intersect this new box with the map. If it fails, reject the movement (direction is irrelevant). If it is accepted, go to a transitional "moving" state. Run cell transition animation and go back to standard testing as soon as it's done.

EDIT: engrish, small clarification on numbers involved.

Previously "Krohm"

This topic is closed to new replies.

Advertisement