Public Group

More Collision Problems

This topic is 2009 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

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.

Share on other sites
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?
+Debug it!

Share on other sites
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.

#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

Share on other sites

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.

Share on other sites

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

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; }  Edited by molehill mountaineer

Share on other sites
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.

• 33
• 15
• 23
• 10
• 19