In the prior post I talked about how to generate a minimum translation vector, which allowed us to push objects around by forcing separation.
A situation which can lead to incorrect MTV is containment, which is basically when an object is entirely within the other.
In the situation above the minimum overlap would be the entire length of the purple interval. If this is assumed to be the "depth" of penetration and the bodies were separated by this distance, they would not be entirely separated. The depth needs to be "extended" by the distance between the two end points that are the closest, to get the true penetration distance.
Note that if you are certain this type of situation will not occur, then this code is not necessary. It will be necessary if you need to generate MTV through SAT when checking collisions against line segments (again, you could always code it so that you do not have line segments in your game / simulations, as you could replace it by a rectangle).
Assessing containment
First we need to assess whether there is containment. We can do check this by looking for containment for each interval pair. Looking for containment (ie one interval is entirely within the other) can be implemented by the following method of the Interval class.
containsExclusive(interval) {
return interval.min > this.min && interval.max < this.max;
}
Extending the depth when there is containment
Once we identify containment of overlap, we can adjust the depth as follows. The new code is highlighted in red.
let axes1 = polygonA.getAxes();
for (let i = 0; i < axes1.length; i++) {
const axis = axes1[i];
const intervalA = polygonA.projectVertices(axis);
const intervalB = polygonB.projectVertices(axis);
let o = intervalA.getOverlap(intervalB);
if (o < 0) {
return false;
}
// check for containment
if (intervalA.containsExclusive(intervalB) || intervalB.containsExclusive(intervalA)) {
// if containment exists then get the overlap plus the distance
// to between the two end points that are the closest
const max = Math.abs(intervalA.getMax() - intervalB.getMax());
const min = Math.abs(intervalA.getMin() - intervalB.getMin());
if (max > min) {
// if the min differences is less than the max then we need
// to flip the penetration axis
axis.negate();
o += min;
} else {
o += max;
}
}
if (o < overlap) {
overlap = o;
n = axis;
}
}
CodePen
Useful References
https://dyn4j.org/2010/01/sat/ (this explains it all - I have borrowed heavily on this post for my implementation)
コメント