2021-04-28 14:43:48 +08:00
|
|
|
|
module es {
|
|
|
|
|
|
export class ShapeCollisionsPolygon {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查两个多边形之间的碰撞
|
|
|
|
|
|
* @param first
|
|
|
|
|
|
* @param second
|
|
|
|
|
|
* @param result
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static polygonToPolygon(first: Polygon, second: Polygon, result: CollisionResult): boolean {
|
|
|
|
|
|
let isIntersecting = true;
|
|
|
|
|
|
|
2021-06-29 18:40:34 +08:00
|
|
|
|
const firstEdges = first.edgeNormals;
|
|
|
|
|
|
const secondEdges = second.edgeNormals;
|
2021-04-28 14:43:48 +08:00
|
|
|
|
let minIntervalDistance = Number.POSITIVE_INFINITY;
|
2021-05-24 17:20:27 +08:00
|
|
|
|
let translationAxis = es.Vector2.zero;
|
2021-04-28 14:43:48 +08:00
|
|
|
|
let polygonOffset = Vector2.subtract(first.position, second.position);
|
|
|
|
|
|
let axis: Vector2;
|
|
|
|
|
|
|
|
|
|
|
|
// 循环穿过两个多边形的所有边
|
|
|
|
|
|
for (let edgeIndex = 0; edgeIndex < firstEdges.length + secondEdges.length; edgeIndex++) {
|
|
|
|
|
|
// 1. 找出当前多边形是否相交
|
|
|
|
|
|
// 多边形的归一化轴垂直于缓存给我们的当前边
|
2021-06-29 18:40:34 +08:00
|
|
|
|
axis = edgeIndex < firstEdges.length ? firstEdges[edgeIndex] : secondEdges[edgeIndex - firstEdges.length];
|
2021-04-28 14:43:48 +08:00
|
|
|
|
|
|
|
|
|
|
// 求多边形在当前轴上的投影
|
|
|
|
|
|
let intervalDist = 0;
|
2021-06-29 18:40:34 +08:00
|
|
|
|
let {min: minA, max: maxA} = this.getInterval(axis, first);
|
|
|
|
|
|
const {min: minB, max: maxB} = this.getInterval(axis, second);
|
2021-04-28 14:43:48 +08:00
|
|
|
|
|
|
|
|
|
|
// 将区间设为第二个多边形的空间。由轴上投影的位置差偏移。
|
|
|
|
|
|
let relativeIntervalOffset = Vector2.dot(polygonOffset, axis);
|
2021-06-29 18:40:34 +08:00
|
|
|
|
minA += relativeIntervalOffset;
|
|
|
|
|
|
maxA += relativeIntervalOffset;
|
2021-04-28 14:43:48 +08:00
|
|
|
|
|
|
|
|
|
|
// 检查多边形投影是否正在相交
|
2021-06-29 18:40:34 +08:00
|
|
|
|
intervalDist = this.intervalDistance(minA, maxA, minB, maxB);
|
2021-04-28 14:43:48 +08:00
|
|
|
|
if (intervalDist > 0)
|
|
|
|
|
|
isIntersecting = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 对于多对多数据类型转换,添加一个Vector2?参数称为deltaMovement。为了提高速度,我们这里不使用它
|
|
|
|
|
|
// TODO: 现在找出多边形是否会相交。只要检查速度就行了
|
|
|
|
|
|
|
|
|
|
|
|
// 如果多边形不相交,也不会相交,退出循环
|
|
|
|
|
|
if (!isIntersecting)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查当前间隔距离是否为最小值。如果是,则存储间隔距离和当前距离。这将用于计算最小平移向量
|
|
|
|
|
|
intervalDist = Math.abs(intervalDist);
|
|
|
|
|
|
if (intervalDist < minIntervalDistance) {
|
|
|
|
|
|
minIntervalDistance = intervalDist;
|
2021-06-29 18:40:34 +08:00
|
|
|
|
translationAxis.setTo(axis.x, axis.y);
|
2021-04-28 14:43:48 +08:00
|
|
|
|
|
|
|
|
|
|
if (Vector2.dot(translationAxis, polygonOffset) < 0)
|
2021-06-29 18:40:34 +08:00
|
|
|
|
translationAxis = translationAxis.scale(-1);
|
2021-04-28 14:43:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 利用最小平移向量对多边形进行推入。
|
|
|
|
|
|
result.normal = translationAxis;
|
2021-06-29 18:40:34 +08:00
|
|
|
|
result.minimumTranslationVector = translationAxis.scale(minIntervalDistance * -1);
|
2021-04-28 14:43:48 +08:00
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 计算一个多边形在一个轴上的投影,并返回一个[min,max]区间
|
|
|
|
|
|
* @param axis
|
|
|
|
|
|
* @param polygon
|
|
|
|
|
|
* @param min
|
|
|
|
|
|
* @param max
|
|
|
|
|
|
*/
|
2021-06-29 18:40:34 +08:00
|
|
|
|
public static getInterval(axis: Vector2, polygon: Polygon): {min: number, max: number} {
|
|
|
|
|
|
const res = {min: 0, max: 0};
|
|
|
|
|
|
let dot: number;
|
|
|
|
|
|
dot = Vector2.dot( polygon.points[0], axis);
|
|
|
|
|
|
res.max = dot;
|
|
|
|
|
|
res.min = dot;
|
2021-04-28 14:43:48 +08:00
|
|
|
|
for (let i = 1; i < polygon.points.length; i++) {
|
|
|
|
|
|
dot = Vector2.dot(polygon.points[i], axis);
|
2021-06-29 18:40:34 +08:00
|
|
|
|
if (dot < res.min) {
|
|
|
|
|
|
res.min = dot;
|
|
|
|
|
|
} else if (dot > res.max) {
|
|
|
|
|
|
res.max = dot;
|
2021-04-28 14:43:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-06-29 18:40:34 +08:00
|
|
|
|
return res;
|
2021-04-28 14:43:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的
|
|
|
|
|
|
* @param minA
|
|
|
|
|
|
* @param maxA
|
|
|
|
|
|
* @param minB
|
|
|
|
|
|
* @param maxB
|
|
|
|
|
|
*/
|
2021-06-29 18:40:34 +08:00
|
|
|
|
public static intervalDistance(minA: number, maxA: number, minB: number, maxB: number) {
|
2021-04-28 14:43:48 +08:00
|
|
|
|
if (minA < minB)
|
|
|
|
|
|
return minB - maxA;
|
|
|
|
|
|
|
|
|
|
|
|
return minA - maxB;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|