More Collision Problems

Started by
4 comments, last by SelethD 11 years, 5 months ago
I have recently returned to my platformer, and am having more collision woes.

I am moving the player by its velocity, iterating through intersected solid objects looking for the minimum penetration axis, and reversing the movement.

However when in mid air, and pushing against a wall - it gets stuck and vibrates.

Has anyone had similar problems?
Advertisement
Tell us more about how your coll.detect. algorithm works. By solid objects you mean rectangles/tiles or more complex objects?
What is minimum penetration axis?smile.png
+Debug it!
If you coded a generalized collision solver then your problem is deeply rooted within your system and there is likely nothing we can do, because it is likely a problem related to the whole idea behind your system.
If you coded special cases for being in air etc., then you are wrong by doing so, however it would at least make it easier to search for and fix the problem.

In any case, we can only help you help yourself, and only if you give us enough information to help us help you help yourself.
#1: More information.
#2: In the end you will invariably end up having to debug this yourself since it will be necessary to see run-time debug information which requires the project etc. So you may as well get on this track and single-stepping through the code.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


it gets stuck and vibrates.

That is a common symptom of penetration.

Sometimes the object pushes in so far that simply reversing the direction is not quite enough to make it exit the object, so it just gets stuck. It often happens on border cases where it is just touching in one frame, very close but not enough for collision response, and the next frame it becomes fully embedded.

Part of collision response should be to push it outside the object to avoid penetration.

However when in mid air, and pushing against a wall - it gets stuck and vibrates.

Has anyone had similar problems?


This is quite common, the problem is that your hitregions are instersecting - because of this a collision check it will return true in any direction. It's usually because you "jump over" a hitregion (let's say the hitregion is 1 pixel in front of you, and you move your character 2pixels forward).

In my cruddy ASCII illustration below you will see that if you ask the collision detection system "can rectangle 1 go up?" or "can rectange 1 go left", etc ... it will send back false because the hitRegion is "stuck" inside the other one. In my projects I call this overshoot. Check out the code below to see a simple way of dealing with it.
(I am in no way a professional so please excuse any bad/noobish programming habbits)


/* ____________________
top | | <----------- intersecting rectangle
----------------------|---- 1 |
| |_______|____________|
| |
left | 2 | right
| |
| |
-------------------------
bottom
*/


EDIT: a bit more explanation since a code dump is a bit difficult to work through - basically what you want to do is calculate the amount of pixels that are overlapping and move the hitregion back by that amount. In the code below I do the following:

overshootCorrection.x = -(ballRegion.right - paddleRegion.left);
p_ball->move(overshootCorrection);


first I find out what the amount pixels that are overlapping is (right side of ball is "inside" the paddle so we can determine the overlap by subtracting the right side of the ball with the left side of the paddle). Once I know how many pixels we need to offset with I only need to make this into a negative number so that the ball moves left on the x-axis.
The move function will then adjust the position of the bitmap and hitregion so that the ball and paddle are touching.



void PongPrototype::performCollisionChecks()
{
HitRegion* playerRegion = m_pPlayerPaddle->getHitRegion();
HitRegion* playerBallRegion = m_pPlayerBall->getHitRegion();
HitRegion* enemyRegion = m_pEnemyPaddle->getHitRegion();
HitRegion* enemyBallRegion = m_pEnemyBall->getHitRegion();

//check player collisions against level border and correct if necessary
if(checkBorderCollision(playerRegion))
{
correctPaddleOverschoot(m_pPlayerPaddle);
}
if(checkBorderCollision(enemyRegion))
{
correctPaddleOverschoot(m_pEnemyPaddle);
}

//check ball collision against border
//it's important that we check the ball against paddle (in checkRegionCollision) and not the other way around
//-> because of the way that the collision is calculated a larger hitregion checked against a
//smaller one will not return true.
if(checkBorderCollision(playerBallRegion)) //did ball hit level borders?
{
correctBallovershoot(m_pPlayerBall);
bounceBallOffWall(m_pPlayerBall);
}
if(checkBorderCollision(enemyBallRegion))
{
correctBallovershoot(m_pEnemyBall);
bounceBallOffWall(m_pEnemyBall);
}
//check ball collision against players
if(m_pParentProject->checkRegionCollision(playerBallRegion, playerRegion)) //did ball hit player paddle?
{
bounceBallOffPaddle(m_pPlayerBall, m_pPlayerPaddle);
}
if(m_pParentProject->checkRegionCollision(playerBallRegion, enemyRegion)) //did ball hit player paddle?
{
bounceBallOffPaddle(m_pPlayerBall, m_pEnemyPaddle);
}
if(m_pParentProject->checkRegionCollision(enemyBallRegion, playerRegion)) //did ball hit player paddle?
{
bounceBallOffPaddle(m_pEnemyBall, m_pPlayerPaddle);
}
if(m_pParentProject->checkRegionCollision(enemyBallRegion, enemyRegion)) //did ball hit player paddle?
{
bounceBallOffPaddle(m_pEnemyBall, m_pEnemyPaddle);
}
}

void PongPrototype::correctBallovershoot(Ball* p_ball)
{

floatRect region = p_ball->getHitRegion()->getRegion();

XMFLOAT2 offset;
offset.x = 0.0f;
offset.y = 0.0f;
if(region.left <= 0)
offset.x -= region.left; //negative number so we subtract
if(region.right >= SOLIPSIST.getWindowWidth())
offset.x -= (region.right - SOLIPSIST.getWindowWidth());
//hit top or bottom? adjust overshoot and flip y direction
if(region.bottom <= 0)
offset.y -= region.bottom;
if(region.top >= SOLIPSIST.getWindowHeight())
offset.y -= (region.top - SOLIPSIST.getWindowHeight());

//adjust overshoot
p_ball->move(offset);
}

void PongPrototype::bounceBallOffWall(Ball* p_ball)
{
XMFLOAT2 direction = p_ball->getDirection();
floatRect region = p_ball->getHitRegion()->getRegion();

//hit left or right? adjust overshoot and flip x direction
if(region.left <= 0)
{
direction.x = -direction.x;
}
if(region.right >= SOLIPSIST.getWindowWidth())
{
direction.x = -direction.x;
}
//hit top or bottom? adjust overshoot and flip y direction
if(region.bottom <= 0)
{
direction.y = -direction.y;
}
if(region.top >= SOLIPSIST.getWindowHeight())
{
direction.y = -direction.y;
}
//change direction
p_ball->setDirection(direction);

}
//this function is only called when a collision is already detected, so
//we just have to check one side of the ball each time to see which way to bound it
void PongPrototype::bounceBallOffPaddle(Ball* p_ball, Paddle* p_paddle)
{
XMFLOAT2 overshootCorrection;
overshootCorrection.x = 0.0f;
overshootCorrection.y = 0.0f;
XMFLOAT2 direction = p_ball->getDirection();
floatRect ballRegion = p_ball->getHitRegion()->getRegion();
floatRect paddleRegion = p_paddle->getHitRegion()->getRegion();

//bottom of ball hit
if(ballRegion.bottom <= paddleRegion.top &amp;amp;&amp;amp; ballRegion.bottom >= paddleRegion.bottom)
{
direction.y = -direction.y;
//prevent the ball from being "locked" in the paddle
overshootCorrection.y = paddleRegion.top - ballRegion.bottom;
p_ball->move(overshootCorrection);
//change direction
p_ball->setDirection(direction);
return;
}
//top of ball hit
else if(ballRegion.top <= paddleRegion.top &amp;amp;&amp;amp; ballRegion.top >= paddleRegion.bottom)
{
direction.y = -direction.y;
//prevent the ball from being "locked" in the paddle
overshootCorrection.y = -(ballRegion.top - paddleRegion.bottom);
p_ball->move(overshootCorrection);

//change direction
p_ball->setDirection(direction);
return;
}

//left of ball hit
if(ballRegion.left >= paddleRegion.left &amp;amp;&amp;amp; ballRegion.left <= paddleRegion.right)
{
direction.x = -direction.x;
//prevent the ball from being "locked" in the paddle
overshootCorrection.x = ballRegion.left - paddleRegion.right;
p_ball->move(overshootCorrection);
//change direction
p_ball->setDirection(direction);
return;
}
//right of ball hit
else if(ballRegion.right >= paddleRegion.left &amp;amp;&amp;amp; ballRegion.right <= paddleRegion.right)
{
direction.x = -direction.x;
//prevent the ball from being "locked" in the paddle
overshootCorrection.x = -(ballRegion.right - paddleRegion.left);
p_ball->move(overshootCorrection);

//change direction
p_ball->setDirection(direction);
return;
}
}

//The purpose of this function is to keep the paddle within the confines of the WIN32 window
//and prevent the player from moving his paddle out of sight
void PongPrototype::correctPaddleOverschoot(Paddle* p_paddle)
{
XMFLOAT2 offset;
offset.x = 0.0f;
offset.y = 0.0f;
int windowWidth = SOLIPSIST.getWindow()->getWidth();
int windowHeight = SOLIPSIST.getWindow()->getHeight();
floatRect region = p_paddle->getHitRegion()->getRegion();
//store overshoot corrections
if(region.left < 0)
offset.x -= region.left; //subtract because region.left is a negative value
if(region.bottom < 0)
offset.y -= region.bottom;
if(region.right > windowWidth)
offset.x -= (region.right - windowWidth);

if(region.top > windowHeight)
offset.y -= (region.right - windowHeight);

p_paddle->move(offset);
}
//check collision against edges of window
bool PongPrototype::checkBorderCollision(HitRegion* p_region)
{
bool collision = false;
floatRect rect = p_region->getRegion(); //retrieve the location of the hitregion (HitRegion is a wrapper for floatRect)
//check the border of the map and adjust any overshoots
if( rect.bottom <= 0 || rect.top >= SOLIPSIST.getWindow()->getHeight()
|| rect.left <= 0 || rect.right >= SOLIPSIST.getWindow()->getWidth())
collision = true;

return collision;
}
I have found that when doing collisions, if I forget and do an >= instead of a > in my if statements, I will get trapped in the 'solid' boject, by only 1 pixel, and get the 'stuck / vibrating' scenario you described.

So double check your logic.

This topic is closed to new replies.

Advertisement