I have a problem with collision resolution with my tilemap. I have a ball which collides off the tiles. The collisions work fine, except for when the collision happens between two tiles. Then my collision resolution gets glitchy, and the ball flies in a wrong direction. My ball is a rectangle, and the tiles are rectangles.
Here is a .gif which demonstrates the problem:

The ball should reflect off the tiles one at a time.
The algorithm works like this:
- Apply movement, after which check and resolve collisions.
- Find out the tiles the ball overlaps.
- If the tile being checked is not passable:
- Find out how much the ball is overlapping the tile's X and Y axes.
- Resolve collision by moving the ball out of the tile only on the shallow axis. (Whichever axis is least penetrated).
- Move on to the next tile.
- If the tile being checked is passable, do nothing.
I have read that the problem with this minimum-displacement-technique is that after resolving a collision, it is possible that the ball could be moved to another collision. The fix should be, that just run the algorithm second time. I have tried this, but it did not help. Here is my collision handling code, in C++:
void PlayState::handleCollisions() {
// Get ball bounding box.
sf::FloatRect ballBounds = ball.sprite.getGlobalBounds();
// Find out the nearby tiles.
int leftTile = (int)floor((float)ballBounds.left / level1.TILE_WIDTH);
int rightTile = (int)ceil(((float)(ballBounds.left + ballBounds.width) / level1.TILE_WIDTH)) - 1;
int topTile = (int)floor((float)ballBounds.top / level1.TILE_HEIGHT);
int bottomTile = (int)ceil(((float)(ballBounds.top + ballBounds.height) / level1.TILE_HEIGHT)) - 1;
// For each potentially colliding tile,
for(int y = topTile; y <= bottomTile; ++y) {
for(int x = leftTile; x <= rightTile; ++x) {
// If this tile is collidable,
TileCollision collision = getCollision(x, y);
if(collision == Impassable) {
// Determine collision depth (with direction) and magnitude.
sf::FloatRect tileBounds = getTileBounds(x, y);
sf::Vector2f depth = Collision::getIntersectionDepth(ballBounds, tileBounds);
if(depth != sf::Vector2f(0, 0)) {
float absDepthX = std::abs(depth.x);
float absDepthY = std::abs(depth.y);
// Resolve the collision along the shallow axis.
if(absDepthY < absDepthX) {
// Resolve the collision along the Y axis.
ball.sprite.setPosition(ball.sprite.getPosition().x, ball.sprite.getPosition().y + depth.y);
// Perform further collisions with the new bounds.
sf::FloatRect ballBounds = ball.sprite.getGlobalBounds();
// Y-distance to the tile center.
if(distanceY < 0) {
std::cout << "Collided from TOP." << std::endl;
ball.velocity.y = -ball.velocity.y;
}
else {
std::cout << "Collided from BOTTOM." << std::endl;
ball.velocity.y = -ball.velocity.y;
}
}
else {
// Resolve the collision along the X axis.
ball.sprite.setPosition(ball.sprite.getPosition().x + depth.x, ball.sprite.getPosition().y);
// Perform further collisions with the new bounds.
sf::FloatRect ballBounds = ball.sprite.getGlobalBounds();
// X-distance to the tile center.
if(distanceX < 0) {
std::cout << "Collided from LEFT." << std::endl;
ball.velocity.x = -ball.velocity.x;
}
else {
std::cout << "Collided from RIGHT." << std::endl;
ball.velocity.x = -ball.velocity.x;
}
}
}
}
}
}
}
I tried to run the algorithm second time, like this:
void PlayState::update(sf::Time deltaTime) {
ball.update(deltaTime);
handleCollisions();
handleCollisions();
}
What can I do fix this?