solid rectangle collision

The Partridge Family were neither partridges nor a family. Discuss.
ernie_avina
Posts: 4
Joined: September 5th, 2020, 1:01 am

solid rectangle collision

so i understand how to detect collision thats not the problem. the problem comes when i try to make a wall(rectangle) and have another rectangle controlled by me to just not go through it. ive tried so many different frustrating ideas but nothing works. yes ive searched the internet all i ever see is the detection but not the clamping. can anyone help or atleast show me in the right direction. please and thank you.(also im not talking about clamping to the screen)

albinopapa
Posts: 4266
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

Well, the old school chili way involves a lot of if statements, but easy to understand I suppose:

First, you must determine the velocity of the object moving:
if the object is moving left, then collision with objects to the right but collides with their right side
if the object is moving right, then collision with objects to the left but collides with their left side
if the object is moving down, then collision with objects below it but collides with their tops
if the object is moving up, then collision with objects above it but collides with their bottoms

So, the first set of if statements is which direction rectA is heading

Code: Select all

``````if( rectA.velocity.x > 0.f ) {
if( rectA.velocity.y > 0.f ) {
// velocity is ( +speed, +speed ) test against rectB.top and rectB.left
}
else if( rectA.velocity.y < 0.f ) {
// velocity is (+speed, -speed ) test against rectB.bottom and rectB.left
}
else{
// velocity is (+speed, 0 ) test against rectB.left
}
}
else if( rectA.velocity.x < 0.f ) {
if( rectA.velocity.y > 0.f ) {
// velocity is ( -speed, +speed ) test against rectB.top and rectB.right
}
else if( rectA.velocity.y < 0.f ) {
// velocity is ( -speed, -speed ) test against rectB.bottom and rectB.right
}
else{
// velocity is ( -speed, 0 ) test against rectB.right
}
}
else {
if( rectA.velocity.y > 0.f ) {
// velocity is ( 0, +speed ) test against rectB.top
}
else if( rectA.velocity.y < 0.f ) {
// velocity is ( 0, -speed ) test against rectB.bottom
}
else{
// velocity is ( 0, 0 ), shouldn't be any collision
}
}
``````
This narrows down which side or sides of rectB are most likely to affect collision correction.
Once you have that narrowed down, check to see how far the rectangles overlap.

Code: Select all

``````// rectA.left and rectB.right ( rectA is moving left )
auto penetration_move_left = rectB.right - rectA.left;
// rectA.right and rectB.left ( rectA is moving right )
auto penetration_move_right = rectB.right - rectA.left;
// rectA.bottom and rectB.top ( rectA is moving down )
auto penetration_move_down = rectB.bottom - rectA.top;
// rectA.top and rectB.bottom ( rectA is moving up )
auto penetration_move_up = rectB.bottom - rectA.top;
``````
Now, that you have the penetration and the direction rectA is heading, you can move the position by the penetration amount if only one rect needs to move, or split the difference if both needs to move.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4266
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

Code: Select all

``````if( rectA.velocity.x > 0.f ) {
if( rectA.velocity.y > 0.f ) {
auto penetration_move_right = rectB.left- rectA.right;
auto penetration_move_down = rectB.top - rectA.bottom;
// coming from the right and top, move rectA right and up
rectA.left += std::abs( penetration_move_right );
rectA.right += std::abs( penetration_move_right );
rectA.top += penetration_move_down;
rectA.bottom += penetration_move_down;
}
else if( rectA.velocity.y < 0.f ) {
auto penetration_move_right = rectB.left- rectA.right;
auto penetration_move_up = rectB.bottom - rectA.top;
rectA.left += std::abs( penetration_move_right );
rectA.right += std::abs( penetration_move_right );
rectA.top += std::abs( penetration_move_up );
rectA.bottom += std::abs( penetration_move_up );
}
else{
auto penetration_move_right = rectB.left- rectA.right;
rectA.left += std::abs( penetration_move_right );
rectA.right += std::abs( penetration_move_right );
}
}
else if( rectA.velocity.x < 0.f ) {
if( rectA.velocity.y > 0.f ) {
auto penetration_move_left = rectB.right - rectA.left;
auto penetration_move_down = rectB.top - rectA.bottom;
rectA.left += penetration_move_right;
rectA.right += penetration_move_right;
rectA.top += penetration_move_up ;
rectA.bottom += penetration_move_up;
}
else if( rectA.velocity.y < 0.f ) {
auto penetration_move_left = rectB.right - rectA.left;
auto penetration_move_up = rectB.bottom - rectA.top;
rectA.left += penetration_move_right;
rectA.right += penetration_move_right;
rectA.top += std::abs( penetration_move_up );
rectA.bottom += std::abs( penetration_move_up );
}
else{
auto penetration_move_left = rectB.right - rectA.left;
}
}
else {
if( rectA.velocity.y > 0.f ) {
auto penetration_move_down = rectB.top - rectA.bottom;
rectA.top += penetration_move_up ;
rectA.bottom += penetration_move_up;
}
else if( rectA.velocity.y < 0.f ) {
auto penetration_move_up = rectB.bottom - rectA.top;
rectA.top += std::abs( penetration_move_up );
rectA.bottom += std::abs( penetration_move_up );
}
else{
// velocity is ( 0, 0 ), shouldn't be any collision
}
}``````
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4266
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

This is from memory, I know there are some parts I'm leaving out, but this is as correct as I can remember.

Another thing to look up though is separating axis theorem ( SAT ). The tutorials that I have found usually also calculate the amount of penetration so you can calculate the direction and distance you need to move rectA to not overlap.

If you are just looking to keep the block from going off screen however, then this will work:

Code: Select all

``````// assuming the block position is top left
if( rectA.left < left_wall.right ) {
rectA.left = left_wall.right;
rectA.right = left_wall.right + rect.width;
}
else if( rectA.right >= right_wall.left ) {
rectA.left = right_wall.left - rectA.width - 1;
rectA.right = right_wall.left - 1;
}

if( rectA.top < top_wall.bottom ) {
rectA.top = top_wall.bottom );
rectA.bottom = top_wall.top + rectA.height;
}
else if( rectA.bottom >= bottom_wall.top ) {
rectA.top = bottom_wall.top - rectA.height - 1;
rectA.bottom = bottom_wall.top - 1;
}
``````
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

ernie_avina
Posts: 4
Joined: September 5th, 2020, 1:01 am

Re: solid rectangle collision

wow thanks for the responses i appreciate that. but im like really early in the beginner tuts. im trying to get the box to clamp to the wall which is a rectangle floating on the screen. when i get the left side to clamp it works but if i go to the other side it goes through and treats it as its clamping to the left side. heres my code

Code: Select all

``````if (playerX < wallX)
{
if (IsColliding(playerX, playerY, playerWidth, playerHeight,
wallX, wallY, wallWidth, wallHeight))
{
playerX = wallX - playerWidth;
}
}
``````
the isColliding is the bool to check collision. hopefully this helps get a clearer answer thanks.

albinopapa
Posts: 4266
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

Well, not sure about the IsColliding() function since you didn't post that part, so the error could be in there. Assuming it returns true if playerX >= wallX - playerWidth ( or playerX + playerWidth >= wallX ) then it should work as expected, so I'm guessing the issue is in the collision function.

Also, make sure that all your variables ( playerX, playerY, playerWidth, playerHeight, wallX, wallY, wallWidth and wallHeight ) are valid which means the X and Y values should be 0 to ScreenWidth or ScreenHeight, and all your width and height values are greater than 0.

If you set a break point at the if( playerX < wallX ) Place cursor on that line and press F9 then press play ( Start Debugging ) your program will stop when it gets to the line with the red dot. Then you can press F11 to take it one line at a time.

If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4266
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

For left wall, test right side of wall and left side of player:

Code: Select all

``````if( playerX < wallX + wallWidth )
playerX = wallX + wallWidth;
``````
For right wall, test left side of wall and right side of player:

Code: Select all

``````if( playerX + playerWidth >= wallX )
playerX = wallX - playerWidth - 1;
``````
-1 to completely separate
playerX = 740
playerWidth = 50
wallX = 790
playerX + playerWidth = 790 which is equal to wallX so there is still a collision
sub tract another 1 and you'll get 739 + playerWidth = 789, no more collision
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

ernie_avina
Posts: 4
Joined: September 5th, 2020, 1:01 am

Re: solid rectangle collision

thanks for replying, sorry im trying to be as detailed as i can be. the collision detection and clamping to the screen is the part that i can do. the issue is here im trying to have the white square just not pass throught the yellow rectangle on all sides. i hope this helps again.thanks
Attachments
Screenshot 2020-09-18 232122.png

albinopapa
Posts: 4266
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

Well, I kind of already answered that in the second and third replies, or at least one method of figuring it out anyway.

First determine which sides of the rectangles are overlapping. In those posts, I used the moving squares velocity or direction of movement.
Then determine how much overlap there is in the X and Y directions. This can be done by subtracting opposite sides of the two rectangles. If the moving square is moving right, the right side of the square minus the left side of the wall or other rectangle will give you the amount of overlap.
Once you have the overlap amounts, you need to determine which side has the greatest amount of overlaps to determine which direction to push the square or rectangles apart. This can be done using the amount of penetration ( overlap ) and the velocity or velocities of the objects. If I recall, the method chili used was
if( xOverlap * vy > yOverlap * vx ) then the moving square needs pushed up or down, otherwise the square needs to be pushed right or left depending on signs of vx and vy and you keep the xOverlap and yOverlap values positive. It's been a few years, so I may not be remembering correctly.
Once you figure out which way the square needs to be pushed to not be overlapping, the last steps are move the square and set velocity to { 0, 0}.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4266
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

Here is some code from another project I was working on a while back that does what I described

Code: Select all

``````std::optional<PhysicsPropertiesOut> HandleCollision( PhysicsPropertiesIn const& _left, PhysicsPropertiesIn const& _right )noexcept
{
Vec2f velocity = _left.can_move ? _left.velocity : _right.velocity;
Vec2f position = _left.can_move ? _left.position : _right.position;

Vec2f penetration;
bool handled = false;

if(_left.boundingbox.Overlaps(_right.boundingbox))
{
if( velocity.y == 0.f )
{
// Moving horizontal
if( velocity.x < 0.f )
{
// hero left, wall right
position.x += ( _right.boundingbox.right - _left.boundingbox.left );
velocity.x = 0.f;
handled = true;
}
else if( velocity.x > 0.f )
{
// hero right, wall left
position.x += ( _right.boundingbox.left - _left.boundingbox.right );
velocity.x = 0.f;
handled = true;
}
}
else
{
if( velocity.x < 0.f )
{
if( velocity.y > 0.f )
{
// Heading left and down
penetration.x = _right.boundingbox.right - _left.boundingbox.left;
penetration.y = _left.boundingbox.bottom - _right.boundingbox.top;
if( penetration.x * velocity.y > penetration.y * -velocity.x )
{
position.y -= penetration.y;
velocity.y = 0.f;
handled = true;
}
else
{
position.x += penetration.x;
velocity.x = 0.f;
handled = true;
}
}
else
{
// Heading left and up
penetration.x = _right.boundingbox.right - _left.boundingbox.left;
penetration.y = _right.boundingbox.bottom - _left.boundingbox.top;

if( penetration.x * -velocity.y > penetration.y * -velocity.x )
{
position.y += penetration.y;
velocity.y = 0.f;
handled = true;
}
else
{
position.x += penetration.x;
velocity.x = 0.f;
handled = true;
}
}
}
else if( velocity.x > 0.f )
{
if( velocity.y > 0.f )
{
// Heading right and down
penetration = _left.boundingbox.RightBottom() - _right.boundingbox.LeftTop();
if( penetration.x * velocity.y > penetration.y * velocity.x )
{
// hero bottom, wall top
position.y -= penetration.y;
velocity.y = 0.f;
handled = true;
}
else
{
// hero right, wall left
position.x -= penetration.x;
velocity.x = 0.f;
handled = true;
}
}
else if( velocity.y < 0.f )
{
// Heading right and up
penetration.x = _left.boundingbox.right - _right.boundingbox.left;
penetration.y = _right.boundingbox.bottom - _left.boundingbox.top;
if( penetration.x * -velocity.y > penetration.y * velocity.x )
{
// hero top, wall bottom
position.y += penetration.y;
velocity.y = 0.f;
handled = true;
}
else
{
// hero right, wall left
position.x -= penetration.x;
velocity.x = 0.f;
handled = true;
}
}
}
else
{
// Moving vertical
if( velocity.y < 0.f )
{
// hero top, wall bottom
position.y += ( _right.boundingbox.bottom - _left.boundingbox.top );
velocity.y = 0.f;
handled = true;
}
else
{
// hero bottom, wall top
position.y += ( _right.boundingbox.top - _left.boundingbox.bottom );
velocity.y = 0.f;
handled = true;
}
}
}
}``````
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com