shapeCollisions拆分

This commit is contained in:
yhh
2021-04-28 14:43:48 +08:00
parent bc6920f829
commit 9fa0442b20
12 changed files with 821 additions and 899 deletions

View File

@@ -3293,15 +3293,15 @@ declare module es {
radius: number;
_originalRadius: number;
constructor(radius: number);
recalculateBounds(collider: es.Collider): void;
recalculateBounds(collider: Collider): void;
overlaps(other: Shape): any;
collidesWithShape(other: Shape, result: CollisionResult): boolean;
collidesWithLine(start: es.Vector2, end: es.Vector2, hit: es.RaycastHit): boolean;
collidesWithLine(start: Vector2, end: Vector2, hit: RaycastHit): boolean;
/**
* 获取所提供的点是否在此范围内
* @param point
*/
containsPoint(point: es.Vector2): boolean;
containsPoint(point: Vector2): boolean;
pointCollidesWithShape(point: Vector2, result: CollisionResult): boolean;
}
}
@@ -3351,12 +3351,49 @@ declare module es {
}
}
declare module es {
/**
* 各种形状的碰撞例程
* 大多数人都希望第一个形状位于第二个形状的空间内(即shape1)
* pos应该设置为shape1。pos - shape2.pos)。
*/
class ShapeCollisions {
class ShapeCollisionsBox {
static boxToBox(first: Box, second: Box, result: CollisionResult): boolean;
/**
* 用second检查被deltaMovement移动的框的结果
* @param first
* @param second
* @param movement
* @param hit
*/
static boxToBoxCast(first: Box, second: Box, movement: Vector2, hit: RaycastHit): boolean;
private static minkowskiDifference;
}
}
declare module es {
class ShapeCollisionsCircle {
static circleToCircle(first: Circle, second: Circle, result?: CollisionResult): boolean;
/**
* 适用于中心在框内的圆,也适用于与框外中心重合的圆。
* @param circle
* @param box
* @param result
*/
static circleToBox(circle: Circle, box: Box, result?: CollisionResult): boolean;
static circleToPolygon(circle: Circle, polygon: Polygon, result?: CollisionResult): boolean;
static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2): Vector2;
}
}
declare module es {
class ShapeCollisionsLine {
static lineToPoly(start: Vector2, end: Vector2, polygon: Polygon, hit?: RaycastHit): boolean;
static lineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2, intersection: Vector2): boolean;
static lineToCircle(start: Vector2, end: Vector2, s: Circle, hit: RaycastHit): boolean;
}
}
declare module es {
class ShapeCollisionsPoint {
static pointToCircle(point: Vector2, circle: Circle, result: CollisionResult): boolean;
static pointToBox(point: Vector2, box: Box, result?: CollisionResult): boolean;
static pointToPoly(point: Vector2, poly: Polygon, result?: CollisionResult): boolean;
}
}
declare module es {
class ShapeCollisionsPolygon {
/**
* 检查两个多边形之间的碰撞
* @param first
@@ -3364,14 +3401,6 @@ declare module es {
* @param result
*/
static polygonToPolygon(first: Polygon, second: Polygon, result: CollisionResult): boolean;
/**
* 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的
* @param minA
* @param maxA
* @param minB
* @param maxB
*/
static intervalDistance(minA: number, maxA: number, minB: number, maxB: any): number;
/**
* 计算一个多边形在一个轴上的投影,并返回一个[minmax]区间
* @param axis
@@ -3381,67 +3410,13 @@ declare module es {
*/
static getInterval(axis: Vector2, polygon: Polygon, min: Ref<number>, max: Ref<number>): void;
/**
*
* @param circle
* @param polygon
* @param result
* 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的
* @param minA
* @param maxA
* @param minB
* @param maxB
*/
static circleToPolygon(circle: Circle, polygon: Polygon, result?: CollisionResult): boolean;
/**
* 适用于中心在框内的圆,也适用于与框外中心重合的圆。
* @param circle
* @param box
* @param result
*/
static circleToBox(circle: Circle, box: Box, result?: CollisionResult): boolean;
/**
*
* @param point
* @param circle
* @param result
*/
static pointToCircle(point: Vector2, circle: Circle, result: CollisionResult): boolean;
static pointToBox(point: Vector2, box: Box, result: CollisionResult): boolean;
/**
*
* @param lineA
* @param lineB
* @param closestTo
*/
static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2): Vector2;
/**
*
* @param point
* @param poly
* @param result
*/
static pointToPoly(point: Vector2, poly: Polygon, result: CollisionResult): boolean;
/**
*
* @param first
* @param second
* @param result
*/
static circleToCircle(first: Circle, second: Circle, result?: CollisionResult): boolean;
/**
*
* @param first
* @param second
* @param result
*/
static boxToBox(first: Box, second: Box, result: CollisionResult): boolean;
private static minkowskiDifference;
static lineToPoly(start: Vector2, end: Vector2, polygon: Polygon, hit?: RaycastHit): boolean;
static lineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2, intersection: Vector2): boolean;
static lineToCircle(start: Vector2, end: Vector2, s: Circle, hit: RaycastHit): boolean;
/**
* 用second检查被deltaMovement移动的框的结果
* @param first
* @param second
* @param movement
* @param hit
*/
static boxToBoxCast(first: Box, second: Box, movement: Vector2, hit: RaycastHit): boolean;
static intervalDistance(minA: number, maxA: number, minB: number, maxB: any): number;
}
}
declare module es {

View File

@@ -4202,20 +4202,11 @@ var es;
* @param onlyReturnInitializedComponents
*/
ComponentList.prototype.getComponent = function (type, onlyReturnInitializedComponents) {
// for (let component of this._components) {
// if (component instanceof type)
// return component as T;
// }
var fastList = this.fastComponentsMap.get(type);
if (fastList && fastList.length > 0)
return fastList[0];
// 我们可以选择检查挂起的组件以防addComponent和getComponent在同一个框架中被调用
if (!onlyReturnInitializedComponents) {
// for (let i in this._componentsToAdd) {
// let component = this._componentsToAdd[i];
// if (component instanceof type)
// return component as T;
// }
var fastToAddList = this.fastComponentsToAddMap.get(type);
if (fastToAddList && fastToAddList.length > 0)
return fastToAddList[0];
@@ -4230,21 +4221,9 @@ var es;
ComponentList.prototype.getComponents = function (typeName, components) {
if (!components)
components = [];
// for (let component of this._components) {
// if (component instanceof typeName) {
// components.push(component);
// }
// }
var fastList = this.fastComponentsMap.get(typeName);
if (fastList)
components.concat(fastList);
// 我们还检查了待处理的组件以防在同一帧中调用addComponent和getComponent
// for (let i in this._componentsToAdd) {
// let component = this._componentsToAdd[i];
// if (component instanceof typeName) {
// components.push(component);
// }
// }
var fastToAddList = this.fastComponentsToAddMap.get(typeName);
if (fastToAddList)
components.concat(fastToAddList);
@@ -7982,7 +7961,7 @@ var es;
var j = i + 1;
if (j == points.length)
j = 0;
var closest = es.ShapeCollisions.closestPointOnLine(points[i], points[j], point);
var closest = es.ShapeCollisionsCircle.closestPointOnLine(points[i], points[j], point);
tempDistanceSquared = es.Vector2.distanceSquared(point, closest);
if (tempDistanceSquared < distanceSquared.value) {
distanceSquared.value = tempDistanceSquared;
@@ -8050,9 +8029,9 @@ var es;
Polygon.prototype.overlaps = function (other) {
var result = new es.CollisionResult();
if (other instanceof Polygon)
return es.ShapeCollisions.polygonToPolygon(this, other, result);
return es.ShapeCollisionsPolygon.polygonToPolygon(this, other, result);
if (other instanceof es.Circle) {
if (es.ShapeCollisions.circleToPolygon(other, this, result)) {
if (es.ShapeCollisionsCircle.circleToPolygon(other, this, result)) {
result.invertResult();
return true;
}
@@ -8062,10 +8041,10 @@ var es;
};
Polygon.prototype.collidesWithShape = function (other, result) {
if (other instanceof Polygon) {
return es.ShapeCollisions.polygonToPolygon(this, other, result);
return es.ShapeCollisionsPolygon.polygonToPolygon(this, other, result);
}
if (other instanceof es.Circle) {
if (es.ShapeCollisions.circleToPolygon(other, this, result)) {
if (es.ShapeCollisionsCircle.circleToPolygon(other, this, result)) {
result.invertResult();
return true;
}
@@ -8074,7 +8053,7 @@ var es;
throw new Error("overlaps of Polygon to " + other + " are not supported");
};
Polygon.prototype.collidesWithLine = function (start, end, hit) {
return es.ShapeCollisions.lineToPoly(start, end, this, hit);
return es.ShapeCollisionsLine.lineToPoly(start, end, this, hit);
};
/**
* 本质上,这个算法所做的就是从一个点发射一条射线。
@@ -8095,7 +8074,7 @@ var es;
return isInside;
};
Polygon.prototype.pointCollidesWithShape = function (point, result) {
return es.ShapeCollisions.pointToPoly(point, this, result);
return es.ShapeCollisionsPoint.pointToPoly(point, this, result);
};
return Polygon;
}(es.Shape));
@@ -8163,7 +8142,7 @@ var es;
Box.prototype.collidesWithShape = function (other, result) {
// 特殊情况这一个高性能方式实现其他情况则使用polygon方法检测
if (other instanceof Box && other.isUnrotated) {
return es.ShapeCollisions.boxToBox(this, other, result);
return es.ShapeCollisionsBox.boxToBox(this, other, result);
}
// TODO: 让 minkowski 运行于 cricleToBox
return _super.prototype.collidesWithShape.call(this, other, result);
@@ -8175,7 +8154,7 @@ var es;
};
Box.prototype.pointCollidesWithShape = function (point, result) {
if (this.isUnrotated)
return es.ShapeCollisions.pointToBox(point, this, result);
return es.ShapeCollisionsPoint.pointToBox(point, this, result);
return _super.prototype.pointCollidesWithShape.call(this, point, result);
};
return Box;
@@ -8220,23 +8199,23 @@ var es;
if (other instanceof Circle)
return es.Collisions.circleToCircle(this.position, this.radius, other.position, other.radius);
if (other instanceof es.Polygon)
return es.ShapeCollisions.circleToPolygon(this, other, result);
return es.ShapeCollisionsCircle.circleToPolygon(this, other, result);
throw new Error("overlaps of circle to " + other + " are not supported");
};
Circle.prototype.collidesWithShape = function (other, result) {
if (other instanceof es.Box && other.isUnrotated) {
return es.ShapeCollisions.circleToBox(this, other, result);
return es.ShapeCollisionsCircle.circleToBox(this, other, result);
}
if (other instanceof Circle) {
return es.ShapeCollisions.circleToCircle(this, other, result);
return es.ShapeCollisionsCircle.circleToCircle(this, other, result);
}
if (other instanceof es.Polygon) {
return es.ShapeCollisions.circleToPolygon(this, other, result);
return es.ShapeCollisionsCircle.circleToPolygon(this, other, result);
}
throw new Error("Collisions of Circle to " + other + " are not supported");
};
Circle.prototype.collidesWithLine = function (start, end, hit) {
return es.ShapeCollisions.lineToCircle(start, end, this, hit);
return es.ShapeCollisionsLine.lineToCircle(start, end, this, hit);
};
/**
* 获取所提供的点是否在此范围内
@@ -8246,7 +8225,7 @@ var es;
return (es.Vector2.subtract(point, this.position)).lengthSquared() <= this.radius * this.radius;
};
Circle.prototype.pointCollidesWithShape = function (point, result) {
return es.ShapeCollisions.pointToCircle(point, this, result);
return es.ShapeCollisionsPoint.pointToCircle(point, this, result);
};
return Circle;
}(es.Shape));
@@ -8367,13 +8346,300 @@ var es;
})(es || (es = {}));
var es;
(function (es) {
/**
* 各种形状的碰撞例程
* 大多数人都希望第一个形状位于第二个形状的空间内(即shape1)
* pos应该设置为shape1。pos - shape2.pos)。
*/
var ShapeCollisions = /** @class */ (function () {
function ShapeCollisions() {
var ShapeCollisionsBox = /** @class */ (function () {
function ShapeCollisionsBox() {
}
ShapeCollisionsBox.boxToBox = function (first, second, result) {
var minkowskiDiff = this.minkowskiDifference(first, second);
if (minkowskiDiff.contains(0, 0)) {
// 计算MTV。如果它是零我们就可以称它为非碰撞
result.minimumTranslationVector = minkowskiDiff.getClosestPointOnBoundsToOrigin();
if (result.minimumTranslationVector.equals(es.Vector2.zero))
return false;
result.normal = new es.Vector2(-result.minimumTranslationVector.x, -result.minimumTranslationVector.y);
result.normal.normalize();
return true;
}
return false;
};
/**
* 用second检查被deltaMovement移动的框的结果
* @param first
* @param second
* @param movement
* @param hit
*/
ShapeCollisionsBox.boxToBoxCast = function (first, second, movement, hit) {
// 首先,我们检查是否有重叠。如果有重叠,我们就不做扫描测试
var minkowskiDiff = this.minkowskiDifference(first, second);
if (minkowskiDiff.contains(0, 0)) {
// 计算MTV。如果它是零我们就可以称它为非碰撞
var mtv = minkowskiDiff.getClosestPointOnBoundsToOrigin();
if (mtv.equals(es.Vector2.zero))
return false;
hit.normal = new es.Vector2(-mtv.x);
hit.normal.normalize();
hit.distance = 0;
hit.fraction = 0;
return true;
}
else {
// 射线投射移动矢量
var ray = new es.Ray2D(es.Vector2.zero, new es.Vector2(-movement.x));
var fraction = new es.Ref(0);
if (minkowskiDiff.rayIntersects(ray, fraction) && fraction.value <= 1) {
hit.fraction = fraction.value;
hit.distance = movement.length() * fraction.value;
hit.normal = new es.Vector2(-movement.x, -movement.y);
hit.normal.normalize();
hit.centroid = es.Vector2.add(first.bounds.center, es.Vector2.multiply(movement, new es.Vector2(fraction.value)));
return true;
}
}
return false;
};
ShapeCollisionsBox.minkowskiDifference = function (first, second) {
// 我们需要第一个框的左上角
// 碰撞器只会修改运动的位置所以我们需要用位置来计算出运动是什么。
var positionOffset = es.Vector2.subtract(first.position, es.Vector2.add(first.bounds.location, new es.Vector2(first.bounds.size.x / 2, first.bounds.size.y / 2)));
var topLeft = es.Vector2.subtract(es.Vector2.add(first.bounds.location, positionOffset), second.bounds.max);
var fullSize = es.Vector2.add(first.bounds.size, second.bounds.size);
return new es.Rectangle(topLeft.x, topLeft.y, fullSize.x, fullSize.y);
};
return ShapeCollisionsBox;
}());
es.ShapeCollisionsBox = ShapeCollisionsBox;
})(es || (es = {}));
var es;
(function (es) {
var ShapeCollisionsCircle = /** @class */ (function () {
function ShapeCollisionsCircle() {
}
ShapeCollisionsCircle.circleToCircle = function (first, second, result) {
if (result === void 0) { result = new es.CollisionResult(); }
var distanceSquared = es.Vector2.distanceSquared(first.position, second.position);
var sumOfRadii = first.radius + second.radius;
var collided = distanceSquared < sumOfRadii * sumOfRadii;
if (collided) {
result.normal = es.Vector2.normalize(es.Vector2.subtract(first.position, second.position));
var depth = sumOfRadii - Math.sqrt(distanceSquared);
result.minimumTranslationVector = es.Vector2.multiply(new es.Vector2(-depth), result.normal);
result.point = es.Vector2.add(second.position, es.Vector2.multiply(result.normal, new es.Vector2(second.radius)));
// 这可以得到实际的碰撞点,可能有用也可能没用,所以我们暂时把它留在这里
// let collisionPointX = ((first.position.x * second.radius) + (second.position.x * first.radius)) / sumOfRadii;
// let collisionPointY = ((first.position.y * second.radius) + (second.position.y * first.radius)) / sumOfRadii;
// result.point = new Vector2(collisionPointX, collisionPointY);
return true;
}
return false;
};
/**
* 适用于中心在框内的圆,也适用于与框外中心重合的圆。
* @param circle
* @param box
* @param result
*/
ShapeCollisionsCircle.circleToBox = function (circle, box, result) {
if (result === void 0) { result = new es.CollisionResult(); }
var closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint(circle.position, result.normal);
// 先处理中心在盒子里的圆,如果我们是包含的, 它的成本更低,
if (box.containsPoint(circle.position)) {
result.point = closestPointOnBounds.clone();
// 计算MTV。找出安全的、非碰撞的位置并从中得到MTV
var safePlace = es.Vector2.add(closestPointOnBounds, es.Vector2.multiply(result.normal, new es.Vector2(circle.radius)));
result.minimumTranslationVector = es.Vector2.subtract(circle.position, safePlace);
return true;
}
var sqrDistance = es.Vector2.distanceSquared(closestPointOnBounds, circle.position);
// 看框上的点距圆的半径是否小于圆的半径
if (sqrDistance == 0) {
result.minimumTranslationVector = es.Vector2.multiply(result.normal, new es.Vector2(circle.radius));
}
else if (sqrDistance <= circle.radius * circle.radius) {
result.normal = es.Vector2.subtract(circle.position, closestPointOnBounds);
var depth = result.normal.length() - circle.radius;
result.point = closestPointOnBounds;
es.Vector2Ext.normalize(result.normal);
result.minimumTranslationVector = es.Vector2.multiply(new es.Vector2(depth), result.normal);
return true;
}
return false;
};
ShapeCollisionsCircle.circleToPolygon = function (circle, polygon, result) {
if (result === void 0) { result = new es.CollisionResult(); }
// 圆圈在多边形中的位置坐标
var poly2Circle = es.Vector2.subtract(circle.position, polygon.position);
// 首先,我们需要找到从圆到多边形的最近距离
var distanceSquared = new es.Ref(0);
var closestPoint = es.Polygon.getClosestPointOnPolygonToPoint(polygon.points, poly2Circle, distanceSquared, result.normal);
// 确保距离的平方小于半径的平方,否则我们不会相撞。
// 请注意,如果圆完全包含在多边形中,距离可能大于半径。
// 正因为如此,我们还要确保圆的位置不在多边形内。
var circleCenterInsidePoly = polygon.containsPoint(circle.position);
if (distanceSquared.value > circle.radius * circle.radius && !circleCenterInsidePoly)
return false;
// 算出MTV。我们要注意处理完全包含在多边形中的圆或包含其中心的圆
var mtv;
if (circleCenterInsidePoly) {
mtv = es.Vector2.multiply(result.normal, new es.Vector2(Math.sqrt(distanceSquared.value) - circle.radius));
}
else {
// 如果我们没有距离,这意味着圆心在多边形的边缘上。只需根据它的半径移动它
if (distanceSquared.value == 0) {
mtv = new es.Vector2(result.normal.x * circle.radius, result.normal.y * circle.radius);
}
else {
var distance = Math.sqrt(distanceSquared.value);
mtv = es.Vector2.subtract(new es.Vector2(-1), es.Vector2.subtract(poly2Circle, closestPoint))
.multiply(new es.Vector2((circle.radius - distance) / distance));
}
}
result.minimumTranslationVector = mtv;
result.point = es.Vector2.add(closestPoint, polygon.position);
return true;
};
ShapeCollisionsCircle.closestPointOnLine = function (lineA, lineB, closestTo) {
var v = es.Vector2.subtract(lineB, lineA);
var w = es.Vector2.subtract(closestTo, lineA);
var t = es.Vector2.dot(w, v) / es.Vector2.dot(v, v);
t = es.MathHelper.clamp(t, 0, 1);
return es.Vector2.add(lineA, es.Vector2.multiply(v, new es.Vector2(t)));
};
return ShapeCollisionsCircle;
}());
es.ShapeCollisionsCircle = ShapeCollisionsCircle;
})(es || (es = {}));
var es;
(function (es) {
var ShapeCollisionsLine = /** @class */ (function () {
function ShapeCollisionsLine() {
}
ShapeCollisionsLine.lineToPoly = function (start, end, polygon, hit) {
if (hit === void 0) { hit = new es.RaycastHit(); }
var normal = es.Vector2.zero;
var intersectionPoint = es.Vector2.zero;
var fraction = Number.MAX_VALUE;
var hasIntersection = false;
for (var j = polygon.points.length - 1, i = 0; i < polygon.points.length; j = i, i++) {
var edge1 = es.Vector2.add(polygon.position, polygon.points[j]);
var edge2 = es.Vector2.add(polygon.position, polygon.points[i]);
var intersection = es.Vector2.zero;
if (this.lineToLine(edge1, edge2, start, end, intersection)) {
hasIntersection = true;
// TODO: 这是得到分数的正确和最有效的方法吗?
// 先检查x分数。如果是NaN就用y代替
var distanceFraction = (intersection.x - start.x) / (end.x - start.x);
if (Number.isNaN(distanceFraction) || Number.isFinite(distanceFraction))
distanceFraction = (intersection.y - start.y) / (end.y - start.y);
if (distanceFraction < fraction) {
var edge = es.Vector2.subtract(edge2, edge1);
normal = new es.Vector2(edge.y, -edge.x);
fraction = distanceFraction;
intersectionPoint = intersection;
}
}
}
if (hasIntersection) {
normal.normalize();
var distance = es.Vector2.distance(start, intersectionPoint);
hit.setValuesNonCollider(fraction, distance, intersectionPoint, normal);
return true;
}
return false;
};
ShapeCollisionsLine.lineToLine = function (a1, a2, b1, b2, intersection) {
var b = es.Vector2.subtract(a2, a1);
var d = es.Vector2.subtract(b2, b1);
var bDotDPerp = b.x * d.y - b.y * d.x;
// 如果b*d = 0表示这两条直线平行因此有无穷个交点
if (bDotDPerp == 0)
return false;
var c = es.Vector2.subtract(b1, a1);
var t = (c.x * d.y - c.y * d.x) / bDotDPerp;
if (t < 0 || t > 1)
return false;
var u = (c.x * b.y - c.y * b.x) / bDotDPerp;
if (u < 0 || u > 1)
return false;
intersection = es.Vector2.add(a1, es.Vector2.multiply(new es.Vector2(t), b));
return true;
};
ShapeCollisionsLine.lineToCircle = function (start, end, s, hit) {
// 计算这里的长度并分别对d进行标准化因为如果我们命中了我们需要它来得到分数
var lineLength = es.Vector2.distance(start, end);
var d = es.Vector2.divide(es.Vector2.subtract(end, start), new es.Vector2(lineLength));
var m = es.Vector2.subtract(start, s.position);
var b = es.Vector2.dot(m, d);
var c = es.Vector2.dot(m, m) - s.radius * s.radius;
// 如果r的原点在s之外(c>0)和r指向s (b>0) 则返回
if (c > 0 && b > 0)
return false;
var discr = b * b - c;
// 线不在圆圈上
if (discr < 0)
return false;
// 射线相交圆
hit.fraction = -b - Math.sqrt(discr);
// 如果分数为负数,射线从圈内开始,
if (hit.fraction < 0)
hit.fraction = 0;
hit.point = es.Vector2.add(start, es.Vector2.multiply(new es.Vector2(hit.fraction), d));
hit.distance = es.Vector2.distance(start, hit.point);
hit.normal = es.Vector2.normalize(es.Vector2.subtract(hit.point, s.position));
hit.fraction = hit.distance / lineLength;
return true;
};
return ShapeCollisionsLine;
}());
es.ShapeCollisionsLine = ShapeCollisionsLine;
})(es || (es = {}));
var es;
(function (es) {
var ShapeCollisionsPoint = /** @class */ (function () {
function ShapeCollisionsPoint() {
}
ShapeCollisionsPoint.pointToCircle = function (point, circle, result) {
var distanceSquared = es.Vector2.distanceSquared(point, circle.position);
var sumOfRadii = 1 + circle.radius;
var collided = distanceSquared < sumOfRadii * sumOfRadii;
if (collided) {
result.normal = es.Vector2.normalize(es.Vector2.subtract(point, circle.position));
var depth = sumOfRadii - Math.sqrt(distanceSquared);
result.minimumTranslationVector = es.Vector2.multiply(new es.Vector2(-depth, -depth), result.normal);
result.point = es.Vector2.add(circle.position, es.Vector2.multiply(result.normal, new es.Vector2(circle.radius, circle.radius)));
return true;
}
return false;
};
ShapeCollisionsPoint.pointToBox = function (point, box, result) {
if (result === void 0) { result = new es.CollisionResult(); }
if (box.containsPoint(point)) {
// 在方框的空间里找到点
result.point = box.bounds.getClosestPointOnRectangleBorderToPoint(point, result.normal);
result.minimumTranslationVector = es.Vector2.subtract(point, result.point);
return true;
}
return false;
};
ShapeCollisionsPoint.pointToPoly = function (point, poly, result) {
if (result === void 0) { result = new es.CollisionResult(); }
if (poly.containsPoint(point)) {
var distanceSquared = new es.Ref(0);
var closestPoint = es.Polygon.getClosestPointOnPolygonToPoint(poly.points, es.Vector2.subtract(point, poly.position), distanceSquared, result.normal);
result.minimumTranslationVector = new es.Vector2(result.normal.x * Math.sqrt(distanceSquared.value), result.normal.y * Math.sqrt(distanceSquared.value));
result.point = es.Vector2.add(closestPoint, poly.position);
return true;
}
return false;
};
return ShapeCollisionsPoint;
}());
es.ShapeCollisionsPoint = ShapeCollisionsPoint;
})(es || (es = {}));
var es;
(function (es) {
var ShapeCollisionsPolygon = /** @class */ (function () {
function ShapeCollisionsPolygon() {
}
/**
* 检查两个多边形之间的碰撞
@@ -8381,7 +8647,7 @@ var es;
* @param second
* @param result
*/
ShapeCollisions.polygonToPolygon = function (first, second, result) {
ShapeCollisionsPolygon.polygonToPolygon = function (first, second, result) {
var isIntersecting = true;
var firstEdges = first.edgeNormals.slice();
var secondEdges = second.edgeNormals.slice();
@@ -8434,18 +8700,6 @@ var es;
result.minimumTranslationVector = new es.Vector2(-translationAxis.x * minIntervalDistance, -translationAxis.y * minIntervalDistance);
return true;
};
/**
* 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的
* @param minA
* @param maxA
* @param minB
* @param maxB
*/
ShapeCollisions.intervalDistance = function (minA, maxA, minB, maxB) {
if (minA < minB)
return minB - maxA;
return minA - maxB;
};
/**
* 计算一个多边形在一个轴上的投影,并返回一个[minmax]区间
* @param axis
@@ -8453,7 +8707,7 @@ var es;
* @param min
* @param max
*/
ShapeCollisions.getInterval = function (axis, polygon, min, max) {
ShapeCollisionsPolygon.getInterval = function (axis, polygon, min, max) {
var dot = es.Vector2.dot(polygon.points[0], axis);
min.value = max.value = dot;
for (var i = 1; i < polygon.points.length; i++) {
@@ -8467,298 +8721,20 @@ var es;
}
};
/**
*
* @param circle
* @param polygon
* @param result
* 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的
* @param minA
* @param maxA
* @param minB
* @param maxB
*/
ShapeCollisions.circleToPolygon = function (circle, polygon, result) {
if (result === void 0) { result = new es.CollisionResult(); }
// 圆圈在多边形中的位置坐标
var poly2Circle = es.Vector2.subtract(circle.position, polygon.position);
// 首先,我们需要找到从圆到多边形的最近距离
var distanceSquared = new es.Ref(0);
var closestPoint = es.Polygon.getClosestPointOnPolygonToPoint(polygon.points, poly2Circle, distanceSquared, result.normal);
// 确保距离的平方小于半径的平方,否则我们不会相撞。
// 请注意,如果圆完全包含在多边形中,距离可能大于半径。
// 正因为如此,我们还要确保圆的位置不在多边形内。
var circleCenterInsidePoly = polygon.containsPoint(circle.position);
if (distanceSquared.value > circle.radius * circle.radius && !circleCenterInsidePoly)
return false;
// 算出MTV。我们要注意处理完全包含在多边形中的圆或包含其中心的圆
var mtv;
if (circleCenterInsidePoly) {
mtv = es.Vector2.multiply(result.normal, new es.Vector2(Math.sqrt(distanceSquared.value) - circle.radius));
}
else {
// 如果我们没有距离,这意味着圆心在多边形的边缘上。只需根据它的半径移动它
if (distanceSquared.value == 0) {
mtv = new es.Vector2(result.normal.x * circle.radius, result.normal.y * circle.radius);
}
else {
var distance = Math.sqrt(distanceSquared.value);
mtv = es.Vector2.subtract(new es.Vector2(-1), es.Vector2.subtract(poly2Circle, closestPoint))
.multiply(new es.Vector2((circle.radius - distance) / distance));
}
}
result.minimumTranslationVector = mtv;
result.point = es.Vector2.add(closestPoint, polygon.position);
return true;
ShapeCollisionsPolygon.intervalDistance = function (minA, maxA, minB, maxB) {
if (minA < minB)
return minB - maxA;
return minA - maxB;
};
/**
* 适用于中心在框内的圆,也适用于与框外中心重合的圆。
* @param circle
* @param box
* @param result
*/
ShapeCollisions.circleToBox = function (circle, box, result) {
if (result === void 0) { result = new es.CollisionResult(); }
var closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint(circle.position, result.normal);
// 先处理中心在盒子里的圆,如果我们是包含的, 它的成本更低,
if (box.containsPoint(circle.position)) {
result.point = closestPointOnBounds.clone();
// 计算MTV。找出安全的、非碰撞的位置并从中得到MTV
var safePlace = es.Vector2.add(closestPointOnBounds, es.Vector2.multiply(result.normal, new es.Vector2(circle.radius)));
result.minimumTranslationVector = es.Vector2.subtract(circle.position, safePlace);
return true;
}
var sqrDistance = es.Vector2.distanceSquared(closestPointOnBounds, circle.position);
// 看框上的点距圆的半径是否小于圆的半径
if (sqrDistance == 0) {
result.minimumTranslationVector = es.Vector2.multiply(result.normal, new es.Vector2(circle.radius));
}
else if (sqrDistance <= circle.radius * circle.radius) {
result.normal = es.Vector2.subtract(circle.position, closestPointOnBounds);
var depth = result.normal.length() - circle.radius;
result.point = closestPointOnBounds;
es.Vector2Ext.normalize(result.normal);
result.minimumTranslationVector = es.Vector2.multiply(new es.Vector2(depth), result.normal);
return true;
}
return false;
};
/**
*
* @param point
* @param circle
* @param result
*/
ShapeCollisions.pointToCircle = function (point, circle, result) {
var distanceSquared = es.Vector2.distanceSquared(point, circle.position);
var sumOfRadii = 1 + circle.radius;
var collided = distanceSquared < sumOfRadii * sumOfRadii;
if (collided) {
result.normal = es.Vector2.normalize(es.Vector2.subtract(point, circle.position));
var depth = sumOfRadii - Math.sqrt(distanceSquared);
result.minimumTranslationVector = es.Vector2.multiply(new es.Vector2(-depth, -depth), result.normal);
result.point = es.Vector2.add(circle.position, es.Vector2.multiply(result.normal, new es.Vector2(circle.radius, circle.radius)));
return true;
}
return false;
};
ShapeCollisions.pointToBox = function (point, box, result) {
if (box.containsPoint(point)) {
// 在方框的空间里找到点
result.point = box.bounds.getClosestPointOnRectangleBorderToPoint(point, result.normal);
result.minimumTranslationVector = es.Vector2.subtract(point, result.point);
return true;
}
return false;
};
/**
*
* @param lineA
* @param lineB
* @param closestTo
*/
ShapeCollisions.closestPointOnLine = function (lineA, lineB, closestTo) {
var v = es.Vector2.subtract(lineB, lineA);
var w = es.Vector2.subtract(closestTo, lineA);
var t = es.Vector2.dot(w, v) / es.Vector2.dot(v, v);
t = es.MathHelper.clamp(t, 0, 1);
return es.Vector2.add(lineA, es.Vector2.multiply(v, new es.Vector2(t)));
};
/**
*
* @param point
* @param poly
* @param result
*/
ShapeCollisions.pointToPoly = function (point, poly, result) {
if (poly.containsPoint(point)) {
var distanceSquared = new es.Ref(0);
var closestPoint = es.Polygon.getClosestPointOnPolygonToPoint(poly.points, es.Vector2.subtract(point, poly.position), distanceSquared, result.normal);
result.minimumTranslationVector = new es.Vector2(result.normal.x * Math.sqrt(distanceSquared.value), result.normal.y * Math.sqrt(distanceSquared.value));
result.point = es.Vector2.add(closestPoint, poly.position);
return true;
}
return false;
};
/**
*
* @param first
* @param second
* @param result
*/
ShapeCollisions.circleToCircle = function (first, second, result) {
if (result === void 0) { result = new es.CollisionResult(); }
var distanceSquared = es.Vector2.distanceSquared(first.position, second.position);
var sumOfRadii = first.radius + second.radius;
var collided = distanceSquared < sumOfRadii * sumOfRadii;
if (collided) {
result.normal = es.Vector2.normalize(es.Vector2.subtract(first.position, second.position));
var depth = sumOfRadii - Math.sqrt(distanceSquared);
result.minimumTranslationVector = es.Vector2.multiply(new es.Vector2(-depth), result.normal);
result.point = es.Vector2.add(second.position, es.Vector2.multiply(result.normal, new es.Vector2(second.radius)));
// 这可以得到实际的碰撞点,可能有用也可能没用,所以我们暂时把它留在这里
// let collisionPointX = ((first.position.x * second.radius) + (second.position.x * first.radius)) / sumOfRadii;
// let collisionPointY = ((first.position.y * second.radius) + (second.position.y * first.radius)) / sumOfRadii;
// result.point = new Vector2(collisionPointX, collisionPointY);
return true;
}
return false;
};
/**
*
* @param first
* @param second
* @param result
*/
ShapeCollisions.boxToBox = function (first, second, result) {
var minkowskiDiff = this.minkowskiDifference(first, second);
if (minkowskiDiff.contains(0, 0)) {
// 计算MTV。如果它是零我们就可以称它为非碰撞
result.minimumTranslationVector = minkowskiDiff.getClosestPointOnBoundsToOrigin();
if (result.minimumTranslationVector.equals(es.Vector2.zero))
return false;
result.normal = new es.Vector2(-result.minimumTranslationVector.x, -result.minimumTranslationVector.y);
result.normal.normalize();
return true;
}
return false;
};
ShapeCollisions.minkowskiDifference = function (first, second) {
// 我们需要第一个框的左上角
// 碰撞器只会修改运动的位置所以我们需要用位置来计算出运动是什么。
var positionOffset = es.Vector2.subtract(first.position, es.Vector2.add(first.bounds.location, new es.Vector2(first.bounds.size.x / 2, first.bounds.size.y / 2)));
var topLeft = es.Vector2.subtract(es.Vector2.add(first.bounds.location, positionOffset), second.bounds.max);
var fullSize = es.Vector2.add(first.bounds.size, second.bounds.size);
return new es.Rectangle(topLeft.x, topLeft.y, fullSize.x, fullSize.y);
};
ShapeCollisions.lineToPoly = function (start, end, polygon, hit) {
if (hit === void 0) { hit = new es.RaycastHit(); }
var normal = es.Vector2.zero;
var intersectionPoint = es.Vector2.zero;
var fraction = Number.MAX_VALUE;
var hasIntersection = false;
for (var j = polygon.points.length - 1, i = 0; i < polygon.points.length; j = i, i++) {
var edge1 = es.Vector2.add(polygon.position, polygon.points[j]);
var edge2 = es.Vector2.add(polygon.position, polygon.points[i]);
var intersection = es.Vector2.zero;
if (this.lineToLine(edge1, edge2, start, end, intersection)) {
hasIntersection = true;
// TODO: 这是得到分数的正确和最有效的方法吗?
// 先检查x分数。如果是NaN就用y代替
var distanceFraction = (intersection.x - start.x) / (end.x - start.x);
if (Number.isNaN(distanceFraction) || Number.isFinite(distanceFraction))
distanceFraction = (intersection.y - start.y) / (end.y - start.y);
if (distanceFraction < fraction) {
var edge = es.Vector2.subtract(edge2, edge1);
normal = new es.Vector2(edge.y, -edge.x);
fraction = distanceFraction;
intersectionPoint = intersection;
}
}
}
if (hasIntersection) {
normal.normalize();
var distance = es.Vector2.distance(start, intersectionPoint);
hit.setValuesNonCollider(fraction, distance, intersectionPoint, normal);
return true;
}
return false;
};
ShapeCollisions.lineToLine = function (a1, a2, b1, b2, intersection) {
var b = es.Vector2.subtract(a2, a1);
var d = es.Vector2.subtract(b2, b1);
var bDotDPerp = b.x * d.y - b.y * d.x;
// 如果b*d = 0表示这两条直线平行因此有无穷个交点
if (bDotDPerp == 0)
return false;
var c = es.Vector2.subtract(b1, a1);
var t = (c.x * d.y - c.y * d.x) / bDotDPerp;
if (t < 0 || t > 1)
return false;
var u = (c.x * b.y - c.y * b.x) / bDotDPerp;
if (u < 0 || u > 1)
return false;
intersection = es.Vector2.add(a1, es.Vector2.multiply(new es.Vector2(t), b));
return true;
};
ShapeCollisions.lineToCircle = function (start, end, s, hit) {
// 计算这里的长度并分别对d进行标准化因为如果我们命中了我们需要它来得到分数
var lineLength = es.Vector2.distance(start, end);
var d = es.Vector2.divide(es.Vector2.subtract(end, start), new es.Vector2(lineLength));
var m = es.Vector2.subtract(start, s.position);
var b = es.Vector2.dot(m, d);
var c = es.Vector2.dot(m, m) - s.radius * s.radius;
// 如果r的原点在s之外(c>0)和r指向s (b>0) 则返回
if (c > 0 && b > 0)
return false;
var discr = b * b - c;
// 线不在圆圈上
if (discr < 0)
return false;
// 射线相交圆
hit.fraction = -b - Math.sqrt(discr);
// 如果分数为负数,射线从圈内开始,
if (hit.fraction < 0)
hit.fraction = 0;
hit.point = es.Vector2.add(start, es.Vector2.multiply(new es.Vector2(hit.fraction), d));
hit.distance = es.Vector2.distance(start, hit.point);
hit.normal = es.Vector2.normalize(es.Vector2.subtract(hit.point, s.position));
hit.fraction = hit.distance / lineLength;
return true;
};
/**
* 用second检查被deltaMovement移动的框的结果
* @param first
* @param second
* @param movement
* @param hit
*/
ShapeCollisions.boxToBoxCast = function (first, second, movement, hit) {
// 首先,我们检查是否有重叠。如果有重叠,我们就不做扫描测试
var minkowskiDiff = this.minkowskiDifference(first, second);
if (minkowskiDiff.contains(0, 0)) {
// 计算MTV。如果它是零我们就可以称它为非碰撞
var mtv = minkowskiDiff.getClosestPointOnBoundsToOrigin();
if (mtv.equals(es.Vector2.zero))
return false;
hit.normal = new es.Vector2(-mtv.x);
hit.normal.normalize();
hit.distance = 0;
hit.fraction = 0;
return true;
}
else {
// 射线投射移动矢量
var ray = new es.Ray2D(es.Vector2.zero, new es.Vector2(-movement.x));
var fraction = new es.Ref(0);
if (minkowskiDiff.rayIntersects(ray, fraction) && fraction.value <= 1) {
hit.fraction = fraction.value;
hit.distance = movement.length() * fraction.value;
hit.normal = new es.Vector2(-movement.x, -movement.y);
hit.normal.normalize();
hit.centroid = es.Vector2.add(first.bounds.center, es.Vector2.multiply(movement, new es.Vector2(fraction.value)));
return true;
}
}
return false;
};
return ShapeCollisions;
return ShapeCollisionsPolygon;
}());
es.ShapeCollisions = ShapeCollisions;
es.ShapeCollisionsPolygon = ShapeCollisionsPolygon;
})(es || (es = {}));
var es;
(function (es) {

File diff suppressed because one or more lines are too long

View File

@@ -69,7 +69,7 @@ module es {
public collidesWithShape(other: Shape, result: CollisionResult): boolean {
// 特殊情况这一个高性能方式实现其他情况则使用polygon方法检测
if (other instanceof Box && (other as Box).isUnrotated) {
return ShapeCollisions.boxToBox(this, other, result);
return ShapeCollisionsBox.boxToBox(this, other, result);
}
// TODO: 让 minkowski 运行于 cricleToBox
@@ -86,7 +86,7 @@ module es {
public pointCollidesWithShape(point: es.Vector2, result: es.CollisionResult): boolean {
if (this.isUnrotated)
return ShapeCollisions.pointToBox(point, this, result);
return ShapeCollisionsPoint.pointToBox(point, this, result);
return super.pointCollidesWithShape(point, result);
}

View File

@@ -10,7 +10,7 @@ module es {
this._originalRadius = radius;
}
public recalculateBounds(collider: es.Collider) {
public recalculateBounds(collider: Collider) {
// 如果我们没有旋转或不关心TRS我们使用localOffset作为中心
this.center = collider.localOffset;
@@ -42,41 +42,41 @@ module es {
return Collisions.circleToCircle(this.position, this.radius, other.position, (other as Circle).radius);
if (other instanceof Polygon)
return ShapeCollisions.circleToPolygon(this, other, result);
return ShapeCollisionsCircle.circleToPolygon(this, other, result);
throw new Error(`overlaps of circle to ${other} are not supported`);
}
public collidesWithShape(other: Shape, result: CollisionResult): boolean {
if (other instanceof Box && (other as Box).isUnrotated) {
return ShapeCollisions.circleToBox(this, other, result);
return ShapeCollisionsCircle.circleToBox(this, other, result);
}
if (other instanceof Circle) {
return ShapeCollisions.circleToCircle(this, other, result);
return ShapeCollisionsCircle.circleToCircle(this, other, result);
}
if (other instanceof Polygon) {
return ShapeCollisions.circleToPolygon(this, other, result);
return ShapeCollisionsCircle.circleToPolygon(this, other, result);
}
throw new Error(`Collisions of Circle to ${other} are not supported`);
}
public collidesWithLine(start: es.Vector2, end: es.Vector2, hit: es.RaycastHit): boolean {
return ShapeCollisions.lineToCircle(start, end, this, hit);
public collidesWithLine(start: Vector2, end: Vector2, hit: RaycastHit): boolean {
return ShapeCollisionsLine.lineToCircle(start, end, this, hit);
}
/**
* 获取所提供的点是否在此范围内
* @param point
*/
public containsPoint(point: es.Vector2) {
public containsPoint(point: Vector2) {
return (Vector2.subtract(point, this.position)).lengthSquared() <= this.radius * this.radius;
}
public pointCollidesWithShape(point: Vector2, result: CollisionResult): boolean {
return ShapeCollisions.pointToCircle(point, this, result);
return ShapeCollisionsPoint.pointToCircle(point, this, result);
}
}
}

View File

@@ -172,7 +172,7 @@ module es {
if (j == points.length)
j = 0;
let closest = ShapeCollisions.closestPointOnLine(points[i], points[j], point);
let closest = ShapeCollisionsCircle.closestPointOnLine(points[i], points[j], point);
tempDistanceSquared = Vector2.distanceSquared(point, closest);
if (tempDistanceSquared < distanceSquared.value) {
@@ -259,10 +259,10 @@ module es {
public overlaps(other: Shape) {
let result: CollisionResult = new CollisionResult();
if (other instanceof Polygon)
return ShapeCollisions.polygonToPolygon(this, other, result);
return ShapeCollisionsPolygon.polygonToPolygon(this, other, result);
if (other instanceof Circle) {
if (ShapeCollisions.circleToPolygon(other, this, result)) {
if (ShapeCollisionsCircle.circleToPolygon(other, this, result)) {
result.invertResult();
return true;
}
@@ -275,11 +275,11 @@ module es {
public collidesWithShape(other: Shape, result: CollisionResult): boolean {
if (other instanceof Polygon) {
return ShapeCollisions.polygonToPolygon(this, other, result);
return ShapeCollisionsPolygon.polygonToPolygon(this, other, result);
}
if (other instanceof Circle) {
if (ShapeCollisions.circleToPolygon(other, this, result)) {
if (ShapeCollisionsCircle.circleToPolygon(other, this, result)) {
result.invertResult();
return true;
}
@@ -291,7 +291,7 @@ module es {
}
public collidesWithLine(start: es.Vector2, end: es.Vector2, hit: es.RaycastHit): boolean {
return ShapeCollisions.lineToPoly(start, end, this, hit);
return ShapeCollisionsLine.lineToPoly(start, end, this, hit);
}
/**
@@ -316,7 +316,7 @@ module es {
}
public pointCollidesWithShape(point: Vector2, result: CollisionResult): boolean {
return ShapeCollisions.pointToPoly(point, this, result);
return ShapeCollisionsPoint.pointToPoly(point, this, result);
}
}
}

View File

@@ -1,457 +0,0 @@
module es {
/**
* 各种形状的碰撞例程
* 大多数人都希望第一个形状位于第二个形状的空间内(即shape1)
* pos应该设置为shape1。pos - shape2.pos)。
*/
export class ShapeCollisions {
/**
* 检查两个多边形之间的碰撞
* @param first
* @param second
* @param result
*/
public static polygonToPolygon(first: Polygon, second: Polygon, result: CollisionResult): boolean {
let isIntersecting = true;
let firstEdges = first.edgeNormals.slice();
let secondEdges = second.edgeNormals.slice();
let minIntervalDistance = Number.POSITIVE_INFINITY;
let translationAxis = new Vector2();
let polygonOffset = Vector2.subtract(first.position, second.position);
let axis: Vector2;
// 循环穿过两个多边形的所有边
for (let edgeIndex = 0; edgeIndex < firstEdges.length + secondEdges.length; edgeIndex++) {
// 1. 找出当前多边形是否相交
// 多边形的归一化轴垂直于缓存给我们的当前边
if (edgeIndex < firstEdges.length) {
axis = firstEdges[edgeIndex];
} else {
axis = secondEdges[edgeIndex - firstEdges.length];
}
// 求多边形在当前轴上的投影
let minA = new Ref(0);
let minB = new Ref(0);
let maxA = new Ref(0);
let maxB = new Ref(0);
let intervalDist = 0;
this.getInterval(axis, first, minA, maxA);
this.getInterval(axis, second, minB, maxB);
// 将区间设为第二个多边形的空间。由轴上投影的位置差偏移。
let relativeIntervalOffset = Vector2.dot(polygonOffset, axis);
minA.value += relativeIntervalOffset;
maxA.value += relativeIntervalOffset;
// 检查多边形投影是否正在相交
intervalDist = this.intervalDistance(minA.value, maxA.value, minB.value, maxB.value);
if (intervalDist > 0)
isIntersecting = false;
// 对于多对多数据类型转换添加一个Vector2?参数称为deltaMovement。为了提高速度我们这里不使用它
// TODO: 现在找出多边形是否会相交。只要检查速度就行了
// 如果多边形不相交,也不会相交,退出循环
if (!isIntersecting)
return false;
// 检查当前间隔距离是否为最小值。如果是,则存储间隔距离和当前距离。这将用于计算最小平移向量
intervalDist = Math.abs(intervalDist);
if (intervalDist < minIntervalDistance) {
minIntervalDistance = intervalDist;
translationAxis = axis;
if (Vector2.dot(translationAxis, polygonOffset) < 0)
translationAxis = new Vector2(-translationAxis.x, -translationAxis.y);
}
}
// 利用最小平移向量对多边形进行推入。
result.normal = translationAxis;
result.minimumTranslationVector = new Vector2(-translationAxis.x * minIntervalDistance, -translationAxis.y * minIntervalDistance);
return true;
}
/**
* 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的
* @param minA
* @param maxA
* @param minB
* @param maxB
*/
public static intervalDistance(minA: number, maxA: number, minB: number, maxB) {
if (minA < minB)
return minB - maxA;
return minA - maxB;
}
/**
* 计算一个多边形在一个轴上的投影,并返回一个[minmax]区间
* @param axis
* @param polygon
* @param min
* @param max
*/
public static getInterval(axis: Vector2, polygon: Polygon, min: Ref<number>, max: Ref<number>) {
let dot = Vector2.dot(polygon.points[0], axis);
min.value = max.value = dot;
for (let i = 1; i < polygon.points.length; i++) {
dot = Vector2.dot(polygon.points[i], axis);
if (dot < min.value ) {
min.value = dot;
} else if (dot > max.value) {
max.value = dot;
}
}
}
/**
*
* @param circle
* @param polygon
* @param result
*/
public static circleToPolygon(circle: Circle, polygon: Polygon, result: CollisionResult = new CollisionResult()): boolean {
// 圆圈在多边形中的位置坐标
let poly2Circle = Vector2.subtract(circle.position, polygon.position);
// 首先,我们需要找到从圆到多边形的最近距离
let distanceSquared = new Ref(0);
let closestPoint = Polygon.getClosestPointOnPolygonToPoint(polygon.points, poly2Circle, distanceSquared, result.normal);
// 确保距离的平方小于半径的平方,否则我们不会相撞。
// 请注意,如果圆完全包含在多边形中,距离可能大于半径。
// 正因为如此,我们还要确保圆的位置不在多边形内。
let circleCenterInsidePoly = polygon.containsPoint(circle.position);
if (distanceSquared.value > circle.radius * circle.radius && !circleCenterInsidePoly)
return false;
// 算出MTV。我们要注意处理完全包含在多边形中的圆或包含其中心的圆
let mtv: Vector2;
if (circleCenterInsidePoly) {
mtv = Vector2.multiply(result.normal, new Vector2(Math.sqrt(distanceSquared.value) - circle.radius));
} else {
// 如果我们没有距离,这意味着圆心在多边形的边缘上。只需根据它的半径移动它
if (distanceSquared.value == 0) {
mtv = new Vector2(result.normal.x * circle.radius, result.normal.y * circle.radius);
} else {
let distance = Math.sqrt(distanceSquared.value);
mtv = Vector2.subtract(new Vector2(-1), Vector2.subtract(poly2Circle, closestPoint))
.multiply(new Vector2((circle.radius - distance) / distance));
}
}
result.minimumTranslationVector = mtv;
result.point = Vector2.add(closestPoint, polygon.position);
return true;
}
/**
* 适用于中心在框内的圆,也适用于与框外中心重合的圆。
* @param circle
* @param box
* @param result
*/
public static circleToBox(circle: Circle, box: Box, result: CollisionResult = new CollisionResult()): boolean {
let closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint(circle.position, result.normal);
// 先处理中心在盒子里的圆,如果我们是包含的, 它的成本更低,
if (box.containsPoint(circle.position)) {
result.point = closestPointOnBounds.clone();
// 计算MTV。找出安全的、非碰撞的位置并从中得到MTV
let safePlace = Vector2.add(closestPointOnBounds, Vector2.multiply(result.normal, new Vector2(circle.radius)));
result.minimumTranslationVector = Vector2.subtract(circle.position, safePlace);
return true;
}
let sqrDistance = Vector2.distanceSquared(closestPointOnBounds, circle.position);
// 看框上的点距圆的半径是否小于圆的半径
if (sqrDistance == 0) {
result.minimumTranslationVector = Vector2.multiply(result.normal, new Vector2(circle.radius));
} else if (sqrDistance <= circle.radius * circle.radius) {
result.normal = Vector2.subtract(circle.position, closestPointOnBounds);
let depth = result.normal.length() - circle.radius;
result.point = closestPointOnBounds;
Vector2Ext.normalize(result.normal);
result.minimumTranslationVector = Vector2.multiply(new Vector2(depth), result.normal);
return true;
}
return false;
}
/**
*
* @param point
* @param circle
* @param result
*/
public static pointToCircle(point: Vector2, circle: Circle, result: CollisionResult): boolean {
let distanceSquared = Vector2.distanceSquared(point, circle.position);
let sumOfRadii = 1 + circle.radius;
let collided = distanceSquared < sumOfRadii * sumOfRadii;
if (collided) {
result.normal = Vector2.normalize(Vector2.subtract(point, circle.position));
let depth = sumOfRadii - Math.sqrt(distanceSquared);
result.minimumTranslationVector = Vector2.multiply(new Vector2(-depth, -depth), result.normal);
result.point = Vector2.add(circle.position, Vector2.multiply(result.normal, new Vector2(circle.radius, circle.radius)));
return true;
}
return false;
}
public static pointToBox(point: Vector2, box: Box, result: CollisionResult){
if (box.containsPoint(point)){
// 在方框的空间里找到点
result.point = box.bounds.getClosestPointOnRectangleBorderToPoint(point, result.normal);
result.minimumTranslationVector = Vector2.subtract(point, result.point);
return true;
}
return false;
}
/**
*
* @param lineA
* @param lineB
* @param closestTo
*/
public static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2): Vector2 {
let v = Vector2.subtract(lineB, lineA);
let w = Vector2.subtract(closestTo, lineA);
let t = Vector2.dot(w, v) / Vector2.dot(v, v);
t = MathHelper.clamp(t, 0, 1);
return Vector2.add(lineA, Vector2.multiply(v, new Vector2(t)));
}
/**
*
* @param point
* @param poly
* @param result
*/
public static pointToPoly(point: Vector2, poly: Polygon, result: CollisionResult): boolean {
if (poly.containsPoint(point)) {
let distanceSquared = new Ref(0);
let closestPoint = Polygon.getClosestPointOnPolygonToPoint(poly.points, Vector2.subtract(point, poly.position), distanceSquared, result.normal);
result.minimumTranslationVector = new Vector2(result.normal.x * Math.sqrt(distanceSquared.value), result.normal.y * Math.sqrt(distanceSquared.value));
result.point = Vector2.add(closestPoint, poly.position);
return true;
}
return false;
}
/**
*
* @param first
* @param second
* @param result
*/
public static circleToCircle(first: Circle, second: Circle, result: CollisionResult = new CollisionResult()): boolean {
let distanceSquared = Vector2.distanceSquared(first.position, second.position);
let sumOfRadii = first.radius + second.radius;
let collided = distanceSquared < sumOfRadii * sumOfRadii;
if (collided) {
result.normal = Vector2.normalize(Vector2.subtract(first.position, second.position));
let depth = sumOfRadii - Math.sqrt(distanceSquared);
result.minimumTranslationVector = Vector2.multiply(new Vector2(-depth), result.normal);
result.point = Vector2.add(second.position, Vector2.multiply(result.normal, new Vector2(second.radius)));
// 这可以得到实际的碰撞点,可能有用也可能没用,所以我们暂时把它留在这里
// let collisionPointX = ((first.position.x * second.radius) + (second.position.x * first.radius)) / sumOfRadii;
// let collisionPointY = ((first.position.y * second.radius) + (second.position.y * first.radius)) / sumOfRadii;
// result.point = new Vector2(collisionPointX, collisionPointY);
return true;
}
return false;
}
/**
*
* @param first
* @param second
* @param result
*/
public static boxToBox(first: Box, second: Box, result: CollisionResult): boolean {
let minkowskiDiff = this.minkowskiDifference(first, second);
if (minkowskiDiff.contains(0, 0)) {
// 计算MTV。如果它是零我们就可以称它为非碰撞
result.minimumTranslationVector = minkowskiDiff.getClosestPointOnBoundsToOrigin();
if (result.minimumTranslationVector.equals(Vector2.zero))
return false;
result.normal = new Vector2(-result.minimumTranslationVector.x, -result.minimumTranslationVector.y);
result.normal.normalize();
return true;
}
return false;
}
private static minkowskiDifference(first: Box, second: Box): Rectangle {
// 我们需要第一个框的左上角
// 碰撞器只会修改运动的位置所以我们需要用位置来计算出运动是什么。
let positionOffset = Vector2.subtract(first.position, Vector2.add(first.bounds.location, new Vector2(first.bounds.size.x / 2, first.bounds.size.y / 2)));
let topLeft = Vector2.subtract(Vector2.add(first.bounds.location, positionOffset), second.bounds.max);
let fullSize = Vector2.add(first.bounds.size, second.bounds.size);
return new Rectangle(topLeft.x, topLeft.y, fullSize.x, fullSize.y)
}
public static lineToPoly(start: Vector2, end: Vector2, polygon: Polygon, hit: RaycastHit = new RaycastHit()): boolean {
let normal = Vector2.zero;
let intersectionPoint = Vector2.zero;
let fraction = Number.MAX_VALUE;
let hasIntersection = false;
for (let j = polygon.points.length - 1, i = 0; i < polygon.points.length; j = i, i ++){
let edge1 = Vector2.add(polygon.position, polygon.points[j]);
let edge2 = Vector2.add(polygon.position, polygon.points[i]);
let intersection: Vector2 = Vector2.zero;
if (this.lineToLine(edge1, edge2, start, end, intersection)){
hasIntersection = true;
// TODO: 这是得到分数的正确和最有效的方法吗?
// 先检查x分数。如果是NaN就用y代替
let distanceFraction = (intersection.x - start.x) / (end.x - start.x);
if (Number.isNaN(distanceFraction) || Number.isFinite(distanceFraction))
distanceFraction = (intersection.y - start.y) / (end.y - start.y);
if (distanceFraction < fraction){
let edge = Vector2.subtract(edge2, edge1);
normal = new Vector2(edge.y, -edge.x);
fraction = distanceFraction;
intersectionPoint = intersection;
}
}
}
if (hasIntersection){
normal.normalize();
let distance = Vector2.distance(start, intersectionPoint);
hit.setValuesNonCollider(fraction, distance, intersectionPoint, normal);
return true;
}
return false;
}
public static lineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2, intersection: Vector2){
let b = Vector2.subtract(a2, a1);
let d = Vector2.subtract(b2, b1);
let bDotDPerp = b.x * d.y - b.y * d.x;
// 如果b*d = 0表示这两条直线平行因此有无穷个交点
if (bDotDPerp == 0)
return false;
let c = Vector2.subtract(b1, a1);
let t = (c.x * d.y - c.y * d.x) / bDotDPerp;
if (t < 0 || t > 1)
return false;
let u = (c.x * b.y - c.y * b.x) / bDotDPerp;
if (u < 0 || u > 1)
return false;
intersection = Vector2.add(a1, Vector2.multiply(new Vector2(t), b));
return true;
}
public static lineToCircle(start: Vector2, end: Vector2, s: Circle, hit: RaycastHit): boolean{
// 计算这里的长度并分别对d进行标准化因为如果我们命中了我们需要它来得到分数
let lineLength = Vector2.distance(start, end);
let d = Vector2.divide(Vector2.subtract(end, start), new Vector2(lineLength));
let m = Vector2.subtract(start, s.position);
let b = Vector2.dot(m, d);
let c = Vector2.dot(m, m) - s.radius * s.radius;
// 如果r的原点在s之外(c>0)和r指向s (b>0) 则返回
if (c > 0 && b > 0)
return false;
let discr = b * b - c;
// 线不在圆圈上
if (discr < 0)
return false;
// 射线相交圆
hit.fraction = -b - Math.sqrt(discr);
// 如果分数为负数,射线从圈内开始,
if (hit.fraction < 0)
hit.fraction = 0;
hit.point = Vector2.add(start, Vector2.multiply(new Vector2(hit.fraction), d));
hit.distance = Vector2.distance(start, hit.point);
hit.normal = Vector2.normalize(Vector2.subtract(hit.point, s.position));
hit.fraction = hit.distance / lineLength;
return true;
}
/**
* 用second检查被deltaMovement移动的框的结果
* @param first
* @param second
* @param movement
* @param hit
*/
public static boxToBoxCast(first: Box, second: Box, movement: Vector2, hit: RaycastHit): boolean{
// 首先,我们检查是否有重叠。如果有重叠,我们就不做扫描测试
let minkowskiDiff = this.minkowskiDifference(first, second);
if (minkowskiDiff.contains(0, 0)){
// 计算MTV。如果它是零我们就可以称它为非碰撞
let mtv = minkowskiDiff.getClosestPointOnBoundsToOrigin();
if (mtv.equals(Vector2.zero))
return false;
hit.normal = new Vector2(-mtv.x);
hit.normal.normalize();
hit.distance = 0;
hit.fraction = 0;
return true;
}else{
// 射线投射移动矢量
let ray = new Ray2D(Vector2.zero, new Vector2(-movement.x));
let fraction = new Ref(0);
if (minkowskiDiff.rayIntersects(ray, fraction) && fraction.value <= 1){
hit.fraction = fraction.value;
hit.distance = movement.length() * fraction.value;
hit.normal = new Vector2(-movement.x, -movement.y);
hit.normal.normalize();
hit.centroid = Vector2.add(first.bounds.center, Vector2.multiply(movement, new Vector2(fraction.value)));
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,71 @@
module es {
export class ShapeCollisionsBox {
public static boxToBox(first: Box, second: Box, result: CollisionResult): boolean {
let minkowskiDiff = this.minkowskiDifference(first, second);
if (minkowskiDiff.contains(0, 0)) {
// 计算MTV。如果它是零我们就可以称它为非碰撞
result.minimumTranslationVector = minkowskiDiff.getClosestPointOnBoundsToOrigin();
if (result.minimumTranslationVector.equals(Vector2.zero))
return false;
result.normal = new Vector2(-result.minimumTranslationVector.x, -result.minimumTranslationVector.y);
result.normal.normalize();
return true;
}
return false;
}
/**
* 用second检查被deltaMovement移动的框的结果
* @param first
* @param second
* @param movement
* @param hit
*/
public static boxToBoxCast(first: Box, second: Box, movement: Vector2, hit: RaycastHit): boolean {
// 首先,我们检查是否有重叠。如果有重叠,我们就不做扫描测试
let minkowskiDiff = this.minkowskiDifference(first, second);
if (minkowskiDiff.contains(0, 0)) {
// 计算MTV。如果它是零我们就可以称它为非碰撞
let mtv = minkowskiDiff.getClosestPointOnBoundsToOrigin();
if (mtv.equals(Vector2.zero))
return false;
hit.normal = new Vector2(-mtv.x);
hit.normal.normalize();
hit.distance = 0;
hit.fraction = 0;
return true;
} else {
// 射线投射移动矢量
let ray = new Ray2D(Vector2.zero, new Vector2(-movement.x));
let fraction = new Ref(0);
if (minkowskiDiff.rayIntersects(ray, fraction) && fraction.value <= 1) {
hit.fraction = fraction.value;
hit.distance = movement.length() * fraction.value;
hit.normal = new Vector2(-movement.x, -movement.y);
hit.normal.normalize();
hit.centroid = Vector2.add(first.bounds.center, Vector2.multiply(movement, new Vector2(fraction.value)));
return true;
}
}
return false;
}
private static minkowskiDifference(first: Box, second: Box): Rectangle {
// 我们需要第一个框的左上角
// 碰撞器只会修改运动的位置所以我们需要用位置来计算出运动是什么。
let positionOffset = Vector2.subtract(first.position, Vector2.add(first.bounds.location, new Vector2(first.bounds.size.x / 2, first.bounds.size.y / 2)));
let topLeft = Vector2.subtract(Vector2.add(first.bounds.location, positionOffset), second.bounds.max);
let fullSize = Vector2.add(first.bounds.size, second.bounds.size);
return new Rectangle(topLeft.x, topLeft.y, fullSize.x, fullSize.y)
}
}
}

View File

@@ -0,0 +1,108 @@
module es {
export class ShapeCollisionsCircle {
public static circleToCircle(first: Circle, second: Circle, result: CollisionResult = new CollisionResult()): boolean {
let distanceSquared = Vector2.distanceSquared(first.position, second.position);
let sumOfRadii = first.radius + second.radius;
let collided = distanceSquared < sumOfRadii * sumOfRadii;
if (collided) {
result.normal = Vector2.normalize(Vector2.subtract(first.position, second.position));
let depth = sumOfRadii - Math.sqrt(distanceSquared);
result.minimumTranslationVector = Vector2.multiply(new Vector2(-depth), result.normal);
result.point = Vector2.add(second.position, Vector2.multiply(result.normal, new Vector2(second.radius)));
// 这可以得到实际的碰撞点,可能有用也可能没用,所以我们暂时把它留在这里
// let collisionPointX = ((first.position.x * second.radius) + (second.position.x * first.radius)) / sumOfRadii;
// let collisionPointY = ((first.position.y * second.radius) + (second.position.y * first.radius)) / sumOfRadii;
// result.point = new Vector2(collisionPointX, collisionPointY);
return true;
}
return false;
}
/**
* 适用于中心在框内的圆,也适用于与框外中心重合的圆。
* @param circle
* @param box
* @param result
*/
public static circleToBox(circle: Circle, box: Box, result: CollisionResult = new CollisionResult()): boolean {
let closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint(circle.position, result.normal);
// 先处理中心在盒子里的圆,如果我们是包含的, 它的成本更低,
if (box.containsPoint(circle.position)) {
result.point = closestPointOnBounds.clone();
// 计算MTV。找出安全的、非碰撞的位置并从中得到MTV
let safePlace = Vector2.add(closestPointOnBounds, Vector2.multiply(result.normal, new Vector2(circle.radius)));
result.minimumTranslationVector = Vector2.subtract(circle.position, safePlace);
return true;
}
let sqrDistance = Vector2.distanceSquared(closestPointOnBounds, circle.position);
// 看框上的点距圆的半径是否小于圆的半径
if (sqrDistance == 0) {
result.minimumTranslationVector = Vector2.multiply(result.normal, new Vector2(circle.radius));
} else if (sqrDistance <= circle.radius * circle.radius) {
result.normal = Vector2.subtract(circle.position, closestPointOnBounds);
let depth = result.normal.length() - circle.radius;
result.point = closestPointOnBounds;
Vector2Ext.normalize(result.normal);
result.minimumTranslationVector = Vector2.multiply(new Vector2(depth), result.normal);
return true;
}
return false;
}
public static circleToPolygon(circle: Circle, polygon: Polygon, result: CollisionResult = new CollisionResult()): boolean {
// 圆圈在多边形中的位置坐标
let poly2Circle = Vector2.subtract(circle.position, polygon.position);
// 首先,我们需要找到从圆到多边形的最近距离
let distanceSquared = new Ref(0);
let closestPoint = Polygon.getClosestPointOnPolygonToPoint(polygon.points, poly2Circle, distanceSquared, result.normal);
// 确保距离的平方小于半径的平方,否则我们不会相撞。
// 请注意,如果圆完全包含在多边形中,距离可能大于半径。
// 正因为如此,我们还要确保圆的位置不在多边形内。
let circleCenterInsidePoly = polygon.containsPoint(circle.position);
if (distanceSquared.value > circle.radius * circle.radius && !circleCenterInsidePoly)
return false;
// 算出MTV。我们要注意处理完全包含在多边形中的圆或包含其中心的圆
let mtv: Vector2;
if (circleCenterInsidePoly) {
mtv = Vector2.multiply(result.normal, new Vector2(Math.sqrt(distanceSquared.value) - circle.radius));
} else {
// 如果我们没有距离,这意味着圆心在多边形的边缘上。只需根据它的半径移动它
if (distanceSquared.value == 0) {
mtv = new Vector2(result.normal.x * circle.radius, result.normal.y * circle.radius);
} else {
let distance = Math.sqrt(distanceSquared.value);
mtv = Vector2.subtract(new Vector2(-1), Vector2.subtract(poly2Circle, closestPoint))
.multiply(new Vector2((circle.radius - distance) / distance));
}
}
result.minimumTranslationVector = mtv;
result.point = Vector2.add(closestPoint, polygon.position);
return true;
}
public static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2): Vector2 {
let v = Vector2.subtract(lineB, lineA);
let w = Vector2.subtract(closestTo, lineA);
let t = Vector2.dot(w, v) / Vector2.dot(v, v);
t = MathHelper.clamp(t, 0, 1);
return Vector2.add(lineA, Vector2.multiply(v, new Vector2(t)));
}
}
}

View File

@@ -0,0 +1,96 @@
module es {
export class ShapeCollisionsLine {
public static lineToPoly(start: Vector2, end: Vector2, polygon: Polygon, hit: RaycastHit = new RaycastHit()): boolean {
let normal = Vector2.zero;
let intersectionPoint = Vector2.zero;
let fraction = Number.MAX_VALUE;
let hasIntersection = false;
for (let j = polygon.points.length - 1, i = 0; i < polygon.points.length; j = i, i ++){
let edge1 = Vector2.add(polygon.position, polygon.points[j]);
let edge2 = Vector2.add(polygon.position, polygon.points[i]);
let intersection: Vector2 = Vector2.zero;
if (this.lineToLine(edge1, edge2, start, end, intersection)){
hasIntersection = true;
// TODO: 这是得到分数的正确和最有效的方法吗?
// 先检查x分数。如果是NaN就用y代替
let distanceFraction = (intersection.x - start.x) / (end.x - start.x);
if (Number.isNaN(distanceFraction) || Number.isFinite(distanceFraction))
distanceFraction = (intersection.y - start.y) / (end.y - start.y);
if (distanceFraction < fraction){
let edge = Vector2.subtract(edge2, edge1);
normal = new Vector2(edge.y, -edge.x);
fraction = distanceFraction;
intersectionPoint = intersection;
}
}
}
if (hasIntersection){
normal.normalize();
let distance = Vector2.distance(start, intersectionPoint);
hit.setValuesNonCollider(fraction, distance, intersectionPoint, normal);
return true;
}
return false;
}
public static lineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2, intersection: Vector2){
let b = Vector2.subtract(a2, a1);
let d = Vector2.subtract(b2, b1);
let bDotDPerp = b.x * d.y - b.y * d.x;
// 如果b*d = 0表示这两条直线平行因此有无穷个交点
if (bDotDPerp == 0)
return false;
let c = Vector2.subtract(b1, a1);
let t = (c.x * d.y - c.y * d.x) / bDotDPerp;
if (t < 0 || t > 1)
return false;
let u = (c.x * b.y - c.y * b.x) / bDotDPerp;
if (u < 0 || u > 1)
return false;
intersection = Vector2.add(a1, Vector2.multiply(new Vector2(t), b));
return true;
}
public static lineToCircle(start: Vector2, end: Vector2, s: Circle, hit: RaycastHit): boolean{
// 计算这里的长度并分别对d进行标准化因为如果我们命中了我们需要它来得到分数
let lineLength = Vector2.distance(start, end);
let d = Vector2.divide(Vector2.subtract(end, start), new Vector2(lineLength));
let m = Vector2.subtract(start, s.position);
let b = Vector2.dot(m, d);
let c = Vector2.dot(m, m) - s.radius * s.radius;
// 如果r的原点在s之外(c>0)和r指向s (b>0) 则返回
if (c > 0 && b > 0)
return false;
let discr = b * b - c;
// 线不在圆圈上
if (discr < 0)
return false;
// 射线相交圆
hit.fraction = -b - Math.sqrt(discr);
// 如果分数为负数,射线从圈内开始,
if (hit.fraction < 0)
hit.fraction = 0;
hit.point = Vector2.add(start, Vector2.multiply(new Vector2(hit.fraction), d));
hit.distance = Vector2.distance(start, hit.point);
hit.normal = Vector2.normalize(Vector2.subtract(hit.point, s.position));
hit.fraction = hit.distance / lineLength;
return true;
}
}
}

View File

@@ -0,0 +1,45 @@
module es {
export class ShapeCollisionsPoint {
public static pointToCircle(point: Vector2, circle: Circle, result: CollisionResult): boolean {
let distanceSquared = Vector2.distanceSquared(point, circle.position);
let sumOfRadii = 1 + circle.radius;
let collided = distanceSquared < sumOfRadii * sumOfRadii;
if (collided) {
result.normal = Vector2.normalize(Vector2.subtract(point, circle.position));
let depth = sumOfRadii - Math.sqrt(distanceSquared);
result.minimumTranslationVector = Vector2.multiply(new Vector2(-depth, -depth), result.normal);
result.point = Vector2.add(circle.position, Vector2.multiply(result.normal, new Vector2(circle.radius, circle.radius)));
return true;
}
return false;
}
public static pointToBox(point: Vector2, box: Box, result: CollisionResult = new CollisionResult()){
if (box.containsPoint(point)){
// 在方框的空间里找到点
result.point = box.bounds.getClosestPointOnRectangleBorderToPoint(point, result.normal);
result.minimumTranslationVector = Vector2.subtract(point, result.point);
return true;
}
return false;
}
public static pointToPoly(point: Vector2, poly: Polygon, result: CollisionResult = new CollisionResult()): boolean {
if (poly.containsPoint(point)) {
let distanceSquared = new Ref(0);
let closestPoint = Polygon.getClosestPointOnPolygonToPoint(poly.points, Vector2.subtract(point, poly.position), distanceSquared, result.normal);
result.minimumTranslationVector = new Vector2(result.normal.x * Math.sqrt(distanceSquared.value), result.normal.y * Math.sqrt(distanceSquared.value));
result.point = Vector2.add(closestPoint, poly.position);
return true;
}
return false;
}
}
}

View File

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