Sign in to follow this  
Sean_Seanston

Quick Question: Handling Entity-Map Collisions

Recommended Posts

I'm just looking for some input on how exactly to structure collision detection between the game map and entities in a 2D top down shooter.

It should work like e.g. The player moves into an impassable tile, the collision is detected, and the player is moved to the appropriate edge of the tile.

Simple enough in theory, but in terms of the design of the whole system, it gets a bit trickier.
How would you all tend to approach this fairly standard problem?

I've done collision detection before but not like this with a game map etc.

I have a GameWorld class (which oversees the management of entity lists etc.) which I intend to use for collision detection.
I suppose it shouldn't be hard to figure out from an entity's own position what few tiles could possibly be in collision with it so I shouldn't have to test an absurdly high amount of objects against each other.
However... when I do find a collision, what should I do then?
Would it be advisable to call a function in the entity to return its velocity (to work out the side it came from), then call functions in the entity and the tile to get their positions and then finally call a function in the entity to set its new position? All of this within some collision management function in the GameWorld class.

I suppose that sounds good but maybe I just have reservations about using too many getter functions... it's just that I've heard a lot of people look down on them as bad practice in the past but I dunno. Just looking for input to steer me in the right direction design-wise.

Share this post


Link to post
Share on other sites
I've messed around with different collision detection systems a bit and one thing that I have learned it that checking velocity in order to determine collision direction is a bad idea usually. This is because an object could be moving to the right but be hit by another object moving to the right faster than the first object. I would have personally have the game keep track of the previous position of moving objects and use that to determine the direction a collision occurred from. Hope that helps.

( I'm not the most experienced, so if I'm wrong feel free to correct me.)

Share this post


Link to post
Share on other sites
[quote name='LegendofLink' timestamp='1302493023' post='4796923']
I would have personally have the game keep track of the previous position of moving objects and use that to determine the direction a collision occurred from. Hope that helps.[/quote]

Interesting idea... I also already keep track of the previous position of objects as part of my interpolation system.
I'll keep that in mind... thanks for that.

Anyone else?

Share this post


Link to post
Share on other sites
Another thing I've just realized is that different tiles may have different effects when in collision with an entity. For example, one might be impassable but another might slow down movement by 50% or something.

That makes things a little less clear too...

I wrote a function to tell if an entity is in contact with a map tile, now I have to decide how the entity itself and the GameWorld should interact with that. I could just bypass GameWorld for this altogether I suppose and maybe I should...

Either way, it seems like external classes will need to access the tile map data in order to properly decide what to do depending on what characteristics the tile has. Does that seem right?

Share this post


Link to post
Share on other sites
Let the Tile decide, what to do with the Actor it has contact with. Give every actor the same rules, meaning base class or properties, like speed etc. I guess you already have. Now if a Actor steps onto a tile, the Tile gets informed that the Actor is stepping on it. Now you send a "Slow down by 50%" to the Actor. In the Actor, you can then decide to ignore it or apply resistances etc. Give the Actor the Information if the message is coming from a Tile or the player-actor etc. So you can decide if the Actor is allowed to "cheat" by ignoring it or not.
I hope you understand what I mean.
Thats what I would do.

Share this post


Link to post
Share on other sites
Hmmm... makes sense. I like the idea.

I think I'll try that.

That just leaves how to deal with handling the actual collisions then.
I suppose it's straightforward apart from corner collisions. Though by using the old position, I can know both where it is (overlapping) and where it was before it moved.

How can I elegantly deal with all of the collision cases without making a big ugly switch statement? Coming from the left/right/top/bottom should be easy but if the corners overlap then in one example it could've been moving right, up or both directions at once.
I'll mess around and see what I can discover but is there a nice and neat general way to deal with all of that?

Share this post


Link to post
Share on other sites
I cannot really tell, since I am on to figuring out a similar system by myself atm :D :cool:
I will experiment with Chipmunk the next few days, I will see how much work it is to use it for collissions.
I just could not find any decent tutorials yet :P

Share this post


Link to post
Share on other sites
I've been trying to implement a simple test collision detection with walls but I've come into a problem.

It kind of works, but I have a problem when you go diagonally into a wall. Let's say you're below a wall tile, rubbing it, and you try to move up and right. That causes you to slide along to the left side of the tile, when it should just move right.

I know why this is happening... I'm adding the velocities to the player's positions and then checking them against the tiles for validity. So obviously at that point, the player's position is actually above where it was and is now inside the tile, therefore able to approach from the side.

Problem is: I don't really know how to fix it.

Here's my code:
[code]
//Add horizontal velocity
xPos += xvel;

//Add vertical velocity
yPos += yvel;

float eLeft = getX() + getBox().left;
float eRight = getX() + getBox().right;
float eTop = getY() + getBox().top;
float eBottom = getY() + getBox().bottom;

int tileIndexLeft = ( int )eLeft/Tile::SIZE;
int tileIndexRight = ( int )eRight/Tile::SIZE;
int tileIndexTop = ( int )eTop/Tile::SIZE;
int tileIndexBottom = ( int )eBottom/Tile::SIZE;

//Check new position for validity
if( xPos <= -creatureType->getBox().left )
{
xPos = ( float ) -creatureType->getBox().left;
}
else if( xPos >= gameMap.getWidth_T() * Tile::SIZE - creatureType->getBox().right )
{
xPos = ( float )gameMap.getWidth_T() * Tile::SIZE - creatureType->getBox().right;
}

if( yPos >= gameMap.getHeight_T() * Tile::SIZE - creatureType->getBox().top )
{
yPos = ( float )gameMap.getHeight_T() * Tile::SIZE - creatureType->getBox().top;
}
else if( yPos <= 0 - creatureType->getBox().bottom )
{
yPos = ( float ) 0 - creatureType->getBox().bottom;
}

//Check possibly colliding tiles
for( int j = tileIndexBottom; j <= tileIndexTop; j++ )
{
for( int i = tileIndexLeft; i <= tileIndexRight; i++ )
{
Tile* t = gameMap.getTile( j, i );

//Check for overlap with tile
if( gameMap.collision( this, j, i ) )
{
//If tile is not passable
if( !t->getPassable() )
{
//If moving left
if( ( xPos - prevXPos ) < 0 )
{
//Reposition to right of tile
xPos = t->getX() + Tile::SIZE - getBox().left;
}
//If moving right
else if( ( xPos - prevXPos ) > 0 )
{
//Reposition to left of tile
xPos = t->getX() - getBox().right;
}
//If moving down
if( ( yPos - prevYPos ) < 0 )
{
//Reposition above tile
yPos = t->getY() + Tile::SIZE - getBox().bottom;
}
//If moving up
else if( ( yPos - prevYPos ) > 0 )
{
//Reposition below tile
yPos = t->getY() - getBox().top;
}
}
}
}
}
[/code]

This is the move() function. Just before that, the x and y positions are saved in prevXPos and prevYPos.

I can see why it's happening... but I still can't figure out a way to make it not happen. Something about the previous positions I imagine.
While I tinker with it further, can anyone see a non-ugly way of fixing that?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this