Skip to main content
Post Undeleted by DMGregory
Correcting resolution pass
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401

Then we can just pick a new place for that center point, and transform that back into a non-overlapping position for the original rectangle. This lets us resolve the collision in one pass, touching the x coordinate only once and thex or y coordinate, and only once - rather than stacking a vertical collision resolution after a horizontal resolution using outdated info.

We call this the "minimum translation vector" - using the smallest adjustment possible to remove the overlap, whether that's horizontal or vertical.

if(first.Bounds.IntersectsWith(second.Bounds) == false) return; // Compute the center of each rectangle: var firstCenter = new PointF( first.Left + first.Width /2f, first.Top + first.Height /2f ); var secondCenter = new PointF(second.Left + second.Width /2f, second.Top + second.Height/2f); // Compute the offset from the second's center to the first. var offset = firstCenter - secondCenter; // ClampCompute thishow offsetdeeply towe're theoverlapping, closesthorizontally pointand onvertically. var thepenetration enlarged= new PointF((first.Width + second /.Width )/2f,  rectangle's perimeter. First horizontally. (first.Height - second.Height)/2f); if  - new PointF(Math.Abs(offset.x), <Math.Abs(offset.y)); if (first penetration.Widthy +<= second0 || (penetration.Widthx > 0 && penetration.x < penetration.y)/2f) { // If we're only overlapping on the x, // or we're overlapping on the x less than on the y,   // then correct our x position for the minimum safe correction. offset.x = Math.Sign(offset.x) * (first.Width  + second.Width )/2f; } else {  // ...thenOtherwise, vertically. ifour (Math.Abs(offset.y)minimum <safe (first.Heightcorrection +is secondvertical.Height)/2f) offset.y = Math.Sign(offset.y) * (first.Height + second.Height)/2f);2f; } // Now form the top-left corner of the first rectangle at // this new position. var corner = new PointF(secondCenter.x + offset.x - first.Width /2f, secondCenter.y + offset.y - first.Height/2f); first.SetPosition(corner); 

Then we can just pick a new place for that center point, and transform that back into a non-overlapping position for the original rectangle. This lets us resolve the collision in one pass, touching the x coordinate only once and the y coordinate only once.

if(first.Bounds.IntersectsWith(second.Bounds) == false) return; // Compute the center of each rectangle: var firstCenter = new PointF( first.Left + first.Width /2f, first.Top + first.Height /2f ); var secondCenter = new PointF(second.Left + second.Width /2f, second.Top + second.Height/2f); // Compute the offset from the second's center to the first. var offset = firstCenter - secondCenter; // Clamp this offset to the closest point on the enlarged second // rectangle's perimeter. First horizontally... if (Math.Abs(offset.x) < (first.Width + second.Width)/2f) offset.x = Math.Sign(offset.x) * (first.Width + second.Width)/2f; // ...then vertically. if (Math.Abs(offset.y) < (first.Height + second.Height)/2f) offset.y = Math.Sign(offset.y) * (first.Height + second.Height)/2f); // Now form the top-left corner of the first rectangle at // this new position. var corner = new PointF(secondCenter.x + offset.x - first.Width /2f, secondCenter.y + offset.y - first.Height/2f); first.SetPosition(corner); 

Then we can just pick a new place for that center point, and transform that back into a non-overlapping position for the original rectangle. This lets us resolve the collision in one pass, touching only x or y, and only once - rather than stacking a vertical collision resolution after a horizontal resolution using outdated info.

We call this the "minimum translation vector" - using the smallest adjustment possible to remove the overlap, whether that's horizontal or vertical.

if(first.Bounds.IntersectsWith(second.Bounds) == false) return; // Compute the center of each rectangle: var firstCenter = new PointF( first.Left + first.Width /2f, first.Top + first.Height /2f ); var secondCenter = new PointF(second.Left + second.Width /2f, second.Top + second.Height/2f); // Compute the offset from the second's center to the first. var offset = firstCenter - secondCenter; // Compute how deeply we're overlapping, horizontally and vertically. var penetration = new PointF((first.Width + second.Width )/2f,   (first.Height - second.Height)/2f);   - new PointF(Math.Abs(offset.x), Math.Abs(offset.y)); if ( penetration.y <= 0 || (penetration.x > 0 && penetration.x < penetration.y)) { // If we're only overlapping on the x, // or we're overlapping on the x less than on the y,   // then correct our x position for the minimum safe correction. offset.x = Math.Sign(offset.x) * (first.Width  + second.Width )/2f; } else {  // Otherwise, our minimum safe correction is vertical. offset.y = Math.Sign(offset.y) * (first.Height + second.Height)/2f; } // Now form the top-left corner of the first rectangle at // this new position. var corner = new PointF(secondCenter.x + offset.x - first.Width /2f, secondCenter.y + offset.y - first.Height/2f); first.SetPosition(corner); 
Post Deleted by DMGregory
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401

We can make this a bit simpler using Minkowski addition. We can shrink one rectangle down to its center point, and enlarge the other rectangle from its center by the first's width and height, and get an equivalent situation: the two original rectangles overlap if and only if the shrunken point overlaps the new enlarged rectangle.

Then we can just pick a new place for that center point, and transform that back into a non-overlapping position for the original rectangle. This lets us resolve the collision in one pass, touching the x coordinate only once and the y coordinate only once.

if(first.Bounds.IntersectsWith(second.Bounds) == false) return; // Compute the center of each rectangle: var firstCenter = new PointF( first.Left + first.Width /2f, first.Top + first.Height /2f ); var secondCenter = new PointF(second.Left + second.Width /2f, second.Top + second.Height/2f); // Compute the offset from the second's center to the first. var offset = firstCenter - secondCenter; // Clamp this offset to the closest point on the enlarged second // rectangle's perimeter. First horizontally... if (Math.Abs(offset.x) < (first.Width + second.Width)/2f) offset.x = Math.Sign(offset.x) * (first.Width + second.Width)/2f; // ...then vertically. if (Math.Abs(offset.y) < (first.Height + second.Height)/2f) offset.y = Math.Sign(offset.y) * (first.Height + second.Height)/2f); // Now form the top-left corner of the first rectangle at // this new position. var corner = new PointF(secondCenter.x + offset.x - first.Width /2f, secondCenter.y + offset.y - first.Height/2f); first.SetPosition(corner);