框架优化

This commit is contained in:
yhh
2021-07-02 10:11:09 +08:00
parent ea482dab48
commit 3d9c8699e7
31 changed files with 1050 additions and 1105 deletions

View File

@@ -16,20 +16,20 @@ module es {
if (collider.shouldColliderScaleAndRotateWithTransform) {
// 我们只将直线缩放为一个圆,所以我们将使用最大值
let scale = collider.entity.transform.scale;
let hasUnitScale = scale.x == 1 && scale.y == 1;
let maxScale = Math.max(scale.x, scale.y);
const scale = collider.entity.transform.scale;
const hasUnitScale = scale.x === 1 && scale.y === 1;
const maxScale = Math.max(scale.x, scale.y);
this.radius = this._originalRadius * maxScale;
if (collider.entity.transform.rotation != 0) {
if (collider.entity.transform.rotation !== 0) {
// 为了处理偏移原点的旋转,我们只需要将圆心围绕(0,0)在一个圆上移动我们的偏移量就是0角
let offsetAngle = Math.atan2(collider.localOffset.y, collider.localOffset.x) * MathHelper.Rad2Deg;
let offsetLength = hasUnitScale ? collider._localOffsetLength : Vector2.multiply(collider.localOffset, collider.entity.transform.scale).length();
this.center = MathHelper.pointOnCirlce(Vector2.zero, offsetLength, collider.entity.transform.rotationDegrees + offsetAngle);
const offsetAngle = Math.atan2(collider.localOffset.y, collider.localOffset.x) * MathHelper.Rad2Deg;
const offsetLength = hasUnitScale ? collider._localOffsetLength : collider.localOffset.multiply(collider.entity.transform.scale).magnitude();
this.center = MathHelper.pointOnCirlce(Vector2.zero, offsetLength, collider.entity.transform.rotation + offsetAngle);
}
}
this.position = Vector2.add(collider.entity.transform.position, this.center);
this.position = collider.transform.position.add(this.center);
this.bounds = new Rectangle(this.position.x - this.radius, this.position.y - this.radius, this.radius * 2, this.radius * 2);
}
@@ -67,12 +67,19 @@ module es {
return ShapeCollisionsLine.lineToCircle(start, end, this, hit);
}
public getPointAlongEdge(angle: number): Vector2 {
return new Vector2(
this.position.x + this.radius * Math.cos(angle),
this.position.y + this.radius * Math.sin(angle)
);
}
/**
* 获取所提供的点是否在此范围内
* @param point
*/
public containsPoint(point: Vector2) {
return (Vector2.subtract(point, this.position)).lengthSquared() <= this.radius * this.radius;
return (point.sub(this.position)).lengthSquared() <= this.radius * this.radius;
}
public pointCollidesWithShape(point: Vector2, result: CollisionResult): boolean {

View File

@@ -17,15 +17,39 @@ module es {
*/
public point: Vector2 = Vector2.zero;
public reset() {
this.collider = null;
this.normal.setTo(0, 0);
this.minimumTranslationVector.setTo(0, 0);
if (this.point) {
this.point.setTo(0, 0);
}
}
public cloneTo(cr: CollisionResult) {
cr.collider = this.collider;
cr.normal.setTo(this.normal.x, this.normal.y);
cr.minimumTranslationVector.setTo(
this.minimumTranslationVector.x,
this.minimumTranslationVector.y
);
if (this.point) {
if (!cr.point) {
cr.point = new Vector2(0, 0);
}
cr.point.setTo(this.point.x, this.point.y);
}
}
/**
* 改变最小平移向量如果没有相同方向上的运动它将移除平移的x分量。
* @param deltaMovement
*/
public removeHorizontal(deltaMovement: Vector2){
public removeHorizontalTranslation(deltaMovement: Vector2){
// 检查是否需要横向移动,如果需要,移除并固定响应
if (Math.sign(this.normal.x) != Math.sign(deltaMovement.x) || (deltaMovement.x == 0 && this.normal.x != 0)){
let responseDistance = this.minimumTranslationVector.length();
let fix = responseDistance / this.normal.y;
if (Math.sign(this.normal.x) !== Math.sign(deltaMovement.x) || (deltaMovement.x === 0 && this.normal.x !== 0)){
const responseDistance = this.minimumTranslationVector.magnitude();
const fix = responseDistance / this.normal.y;
// 检查一些边界情况。因为我们除以法线 使得x == 1和一个非常小的y这将导致一个巨大的固定值
if (Math.abs(this.normal.x) != 1 && Math.abs(fix) < Math.abs(deltaMovement.y * 3)){
@@ -35,9 +59,8 @@ module es {
}
public invertResult() {
this.minimumTranslationVector = Vector2.negate(this.minimumTranslationVector);
this.normal = Vector2.negate(this.normal);
return this;
this.minimumTranslationVector = this.minimumTranslationVector.negate();
this.normal = this.normal.negate();
}
public toString(){

View File

@@ -114,9 +114,9 @@ module es {
* @param points
*/
public static recenterPolygonVerts(points: Vector2[]) {
let center = this.findPolygonCenter(points);
const center = this.findPolygonCenter(points);
for (let i = 0; i < points.length; i++)
points[i] = Vector2.subtract(points[i], center);
points[i] = points[i].sub(center);
}
/**
@@ -139,13 +139,13 @@ module es {
* @param points
* @param direction
*/
public static getFarthestPointInDirection(points: Vector2[], direction: Vector2): Vector2{
public static getFarthestPointInDirection(points: Vector2[], direction: Vector2): Vector2 {
let index = 0;
let maxDot = Vector2.dot(points[index], direction);
let maxDot = points[index].dot(direction);
for (let i = 1; i < points.length; i ++){
let dot = Vector2.dot(points[i], direction);
if (dot > maxDot){
for (let i = 1; i < points.length; i++) {
let dot = points[i].dot(direction);
if (dot > maxDot) {
maxDot = dot;
index = i;
}
@@ -163,35 +163,35 @@ module es {
* @param distanceSquared
* @param edgeNormal
*/
public static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2, distanceSquared: Ref<number>, edgeNormal: Vector2): Vector2 {
distanceSquared.value = Number.MAX_VALUE;
edgeNormal.x = 0;
edgeNormal.y = 0;
let closestPoint = Vector2.zero;
public static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2): { distanceSquared: number; edgeNormal: Vector2; closestPoint: Vector2 } {
const res = {
distanceSquared: Number.MAX_VALUE,
edgeNormal: Vector2.zero,
closestPoint: Vector2.zero,
};
let tempDistanceSquared = 0;
for (let i = 0; i < points.length; i++) {
let j = i + 1;
if (j == points.length)
if (j === points.length)
j = 0;
let closest = ShapeCollisionsCircle.closestPointOnLine(points[i], points[j], point);
tempDistanceSquared = Vector2.distanceSquared(point, closest);
const closest = ShapeCollisionsCircle.closestPointOnLine(points[i], points[j], point);
tempDistanceSquared = Vector2.sqrDistance(point, closest);
if (tempDistanceSquared < distanceSquared.value) {
distanceSquared.value = tempDistanceSquared;
closestPoint = closest;
if (tempDistanceSquared < res.distanceSquared) {
res.distanceSquared = tempDistanceSquared;
res.closestPoint = closest;
// 求直线的法线
let line = Vector2.subtract(points[j], points[i]);
edgeNormal.x = -line.y;
edgeNormal.y = line.x;
const line = points[j].sub(points[i]);
res.edgeNormal.x = line.y;
res.edgeNormal.y = -line.x;
}
}
Vector2Ext.normalize(edgeNormal);
return closestPoint;
res.edgeNormal = res.edgeNormal.normalize();
return res;
}
/**
@@ -200,11 +200,11 @@ module es {
* @param originalPoints
* @param rotatedPoints
*/
public static rotatePolygonVerts(radians: number, originalPoints: Vector2[], rotatedPoints: Vector2[]){
public static rotatePolygonVerts(radians: number, originalPoints: Vector2[], rotatedPoints: Vector2[]) {
let cos = Math.cos(radians);
let sin = Math.sin(radians);
for (let i = 0; i < originalPoints.length; i ++){
for (let i = 0; i < originalPoints.length; i++) {
let position = originalPoints[i];
rotatedPoints[i] = new Vector2(position.x * cos + position.y * -sin, position.x * sin + position.y * cos);
}
@@ -212,40 +212,63 @@ module es {
public recalculateBounds(collider: Collider) {
// 如果我们没有旋转或不关心TRS我们使用localOffset作为中心我们会从那开始
this.center = collider.localOffset.clone();
this.center = collider.localOffset;
if (collider.shouldColliderScaleAndRotateWithTransform) {
let hasUnitScale = true;
let tempMat: Matrix2D;
let combinedMatrix = Matrix2D.createTranslation(-this._polygonCenter.x, -this._polygonCenter.y);
const tempMat: Matrix2D = new Matrix2D();
const combinedMatrix: Matrix2D = new Matrix2D();
Matrix2D.createTranslation(
this._polygonCenter.x * -1,
this._polygonCenter.y * -1,
combinedMatrix
);
if (!collider.entity.transform.scale.equals(Vector2.one)) {
tempMat = Matrix2D.createScale(collider.entity.transform.scale.x, collider.entity.transform.scale.y);
combinedMatrix = combinedMatrix.multiply(tempMat);
Matrix2D.createScale(
collider.entity.scale.x,
collider.entity.scale.y,
tempMat
);
Matrix2D.multiply(combinedMatrix, tempMat, combinedMatrix);
hasUnitScale = false;
// 缩放偏移量并将其设置为中心。如果我们有旋转,它会在下面重置
this.center = Vector2.multiply(collider.localOffset, collider.entity.transform.scale);
const scaledOffset = new Vector2(
collider.localOffset.x * collider.entity.scale.x,
collider.localOffset.y * collider.entity.scale.y
);
this.center = scaledOffset;
}
if (collider.entity.transform.rotation != 0) {
tempMat = Matrix2D.createRotation(collider.entity.transform.rotation);
combinedMatrix = combinedMatrix.multiply(tempMat);
Matrix2D.createRotation(
MathHelper.Deg2Rad * collider.entity.rotation,
tempMat
);
Matrix2D.multiply(combinedMatrix, tempMat, combinedMatrix);
// 为了处理偏移原点的旋转我们只需要将圆心在(0,0)附近移动
// 我们的偏移使角度为0我们还需要处理这里的比例所以我们先对偏移进行缩放以得到合适的长度。
let offsetAngle = Math.atan2(collider.localOffset.y * collider.entity.transform.scale.y, collider.localOffset.x * collider.entity.transform.scale.x) * MathHelper.Rad2Deg;
let offsetLength = hasUnitScale ? collider._localOffsetLength :
Vector2.multiply(collider.localOffset, collider.entity.transform.scale).length();
const offsetAngle = Math.atan2(collider.localOffset.y * collider.entity.transform.scale.y, collider.localOffset.x * collider.entity.transform.scale.x) * MathHelper.Rad2Deg;
const offsetLength = hasUnitScale ? collider._localOffsetLength :
collider.localOffset.multiply(collider.entity.transform.scale).magnitude();
this.center = MathHelper.pointOnCirlce(Vector2.zero, offsetLength,
collider.entity.transform.rotationDegrees + offsetAngle);
}
tempMat = Matrix2D.createTranslation(this._polygonCenter.x, this._polygonCenter.y);
combinedMatrix = combinedMatrix.multiply(tempMat);
Matrix2D.createTranslation(
this._polygonCenter.x,
this._polygonCenter.y,
tempMat
);
Matrix2D.multiply(combinedMatrix, tempMat, combinedMatrix);
// 最后变换原始点
Vector2Ext.transform(this._originalPoints, combinedMatrix, this.points);
this.points = [];
this._originalPoints.forEach(p => {
this.points.push(p.transform(combinedMatrix));
});
this.isUnrotated = collider.entity.transform.rotation == 0;
@@ -254,9 +277,9 @@ module es {
this._areEdgeNormalsDirty = true;
}
this.position = Vector2.add(collider.entity.transform.position, this.center);
this.position = collider.transform.position.add(this.center);
this.bounds = Rectangle.rectEncompassingPoints(this.points);
this.bounds.location = Vector2.add(this.bounds.location, this.position);
this.bounds.location = this.bounds.location.add(this.position);
}
public overlaps(other: Shape) {
@@ -304,11 +327,11 @@ module es {
*/
public containsPoint(point: Vector2) {
// 将点归一化到多边形坐标空间中
point.sub(this.position);
point = point.sub(this.position);
let isInside = false;
for (let i = 0, j = this.points.length - 1; i < this.points.length; j = i++) {
if (((this.points[i].y > point.y) != (this.points[j].y > point.y)) &&
if (((this.points[i].y > point.y) !== (this.points[j].y > point.y)) &&
(point.x < (this.points[j].x - this.points[i].x) * (point.y - this.points[i].y) / (this.points[j].y - this.points[i].y) +
this.points[i].x)) {
isInside = !isInside;

View File

@@ -1,20 +1,21 @@
module es {
export class RealtimeCollisions {
public static intersectMovingCircleBox(s: Circle, b: Box, movement: Vector2, time: Ref<number>): boolean {
public static intersectMovingCircleBox(s: Circle, b: Box, movement: Vector2, time: number): boolean {
// 计算将b按球面半径r扩大后的AABB
let e = b.bounds.clone();
const e = b.bounds;
e.inflate(s.radius, s.radius);
// 将射线与展开的矩形e相交如果射线错过了e则以无交点退出否则得到交点p和时间t作为结果。
let ray = new Ray2D(Vector2.subtract(s.position, movement), s.position);
if (!e.rayIntersects(ray, time) && time.value > 1)
const ray = new Ray2D(s.position.sub(movement), s.position);
const res = e.rayIntersects(ray);
if (!res.intersected && res.distance > 1)
return false;
// 求交点
let point = Vector2.add(ray.start, Vector2.multiplyScaler(ray.direction, time.value));
const point = ray.start.add(ray.direction.scale(time));
// 计算交点p位于b的哪个最小面和最大面之外。注意u和v不能有相同的位集它们之间必须至少有一个位集。
let u, v = 0;
let u: number, v: number = 0;
if (point.x < b.bounds.left)
u |= 1;
if (point.x > b.bounds.right)
@@ -25,7 +26,7 @@ module es {
v |= 2;
// 'or'将所有的比特集合在一起,形成一个比特掩码(注意u + v == u | v)
let m = u + v;
const m = u + v;
// 如果这3个比特都被设置那么该点就在顶点区域内。
if (m == 3){
@@ -66,8 +67,8 @@ module es {
point = box.bounds.getClosestPointOnRectangleToPoint(cirlce.position);
// 圆和方块相交,如果圆心到点的距离小于圆的半径,则圆和方块相交
let v = Vector2.subtract(point, cirlce.position);
let dist = Vector2.dot(v, v);
const v = point.sub(cirlce.position);
const dist = v.dot(v);
return dist <= cirlce.radius * cirlce.radius;
}

View File

@@ -1,7 +1,7 @@
module es {
export class ShapeCollisionsBox {
public static boxToBox(first: Box, second: Box, result: CollisionResult): boolean {
let minkowskiDiff = this.minkowskiDifference(first, second);
const minkowskiDiff = this.minkowskiDifference(first, second);
if (minkowskiDiff.contains(0, 0)) {
// 计算MTV。如果它是零我们就可以称它为非碰撞
result.minimumTranslationVector = minkowskiDiff.getClosestPointOnBoundsToOrigin();
@@ -9,8 +9,8 @@ module es {
if (result.minimumTranslationVector.equals(Vector2.zero))
return false;
result.normal = new Vector2(-result.minimumTranslationVector.x, -result.minimumTranslationVector.y);
result.normal.normalize();
result.normal = result.minimumTranslationVector.scale(-1);
result.normal = result.normal.normalize();
return true;
}
@@ -27,29 +27,29 @@ module es {
*/
public static boxToBoxCast(first: Box, second: Box, movement: Vector2, hit: RaycastHit): boolean {
// 首先,我们检查是否有重叠。如果有重叠,我们就不做扫描测试
let minkowskiDiff = this.minkowskiDifference(first, second);
const minkowskiDiff = this.minkowskiDifference(first, second);
if (minkowskiDiff.contains(0, 0)) {
// 计算MTV。如果它是零我们就可以称它为非碰撞
let mtv = minkowskiDiff.getClosestPointOnBoundsToOrigin();
const mtv = minkowskiDiff.getClosestPointOnBoundsToOrigin();
if (mtv.equals(Vector2.zero))
return false;
hit.normal = new Vector2(-mtv.x, -mtv.y);
hit.normal.normalize();
hit.normal = hit.normal.normalize();
hit.distance = 0;
hit.fraction = 0;
return true;
} else {
// 射线投射移动矢量
let ray = new Ray2D(Vector2.zero, new Vector2(-movement.x, -movement.y));
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.multiplyScaler(movement, fraction.value));
const ray = new Ray2D(Vector2.zero, movement.scale(-1));
const res = minkowskiDiff.rayIntersects(ray);
if (res.intersected && res.distance <= 1) {
hit.fraction = res.distance;
hit.distance = movement.magnitude() * res.distance;
hit.normal = movement.scale(-1);
hit.normal = hit.normal.normalize();
hit.centroid = first.bounds.center.add(movement.scale(res.distance));
return true;
}
@@ -61,9 +61,9 @@ module es {
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);
const positionOffset = first.position.sub(first.bounds.center);
const topLeft = first.bounds.location.add(positionOffset.sub(second.bounds.max));
const fullSize = first.bounds.size.add(second.bounds.size);
return new Rectangle(topLeft.x, topLeft.y, fullSize.x, fullSize.y)
}

View File

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

View File

@@ -7,10 +7,10 @@ module es {
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)){
const edge1 = Vector2.add(polygon.position, polygon.points[j]);
const edge2 = Vector2.add(polygon.position, polygon.points[i]);
const intersection: Vector2 = Vector2.zero;
if (ShapeCollisionsLine.lineToLine(edge1, edge2, start, end, intersection)){
hasIntersection = true;
// TODO: 这是得到分数的正确和最有效的方法吗?
@@ -20,7 +20,7 @@ module es {
distanceFraction = (intersection.y - start.y) / (end.y - start.y);
if (distanceFraction < fraction){
let edge = Vector2.subtract(edge2, edge1);
const edge = edge2.sub(edge1);
normal = new Vector2(edge.y, -edge.x);
fraction = distanceFraction;
intersectionPoint = intersection;
@@ -29,9 +29,9 @@ module es {
}
if (hasIntersection){
normal.normalize();
let distance = Vector2.distance(start, intersectionPoint);
hit.setValuesNonCollider(fraction, distance, intersectionPoint, normal);
normal = normal.normalize();
const distance = Vector2.distance(start, intersectionPoint);
hit.setValues(fraction, distance, intersectionPoint, normal);
return true;
}
@@ -39,24 +39,24 @@ module es {
}
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;
const b = a2.sub(a1);
const d = b2.sub(b1);
const 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;
const c = b1.sub(a1);
const 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;
const u = (c.x * b.y - c.y * b.x) / bDotDPerp;
if (u < 0 || u > 1)
return false;
let r = Vector2.add(a1, Vector2.multiplyScaler(b, t));
const r = a1.add(b.scale(t));
intersection.x = r.x;
intersection.y = r.y;
@@ -65,11 +65,11 @@ module es {
public static lineToCircle(start: Vector2, end: Vector2, s: Circle, hit: RaycastHit): boolean{
// 计算这里的长度并分别对d进行标准化因为如果我们命中了我们需要它来得到分数
let lineLength = Vector2.distance(start, end);
let d = Vector2.divideScaler(Vector2.subtract(end, start), lineLength);
let m = Vector2.subtract(start, s.position);
let b = Vector2.dot(m, d);
let c = Vector2.dot(m, m) - s.radius * s.radius;
const lineLength = Vector2.distance(start, end);
const d = Vector2.divideScaler(end.sub(start), lineLength);
const m = start.sub(s.position);
const b = m.dot(d);
const c = m.dot(m) - s.radius * s.radius;
// 如果r的原点在s之外(c>0)和r指向s (b>0) 则返回
if (c > 0 && b > 0)
@@ -87,9 +87,9 @@ module es {
if (hit.fraction < 0)
hit.fraction = 0;
hit.point = Vector2.add(start, Vector2.multiplyScaler(d, hit.fraction));
hit.point = start.add(d.scale(hit.fraction));
hit.distance = Vector2.distance(start, hit.point);
hit.normal = Vector2.normalize(Vector2.subtract(hit.point, s.position));
hit.normal = hit.point.sub(s.position).normalize();
hit.fraction = hit.distance / lineLength;
return true;

View File

@@ -1,14 +1,14 @@
module es {
export class ShapeCollisionsPoint {
public static pointToCircle(point: Vector2, circle: Circle, result: CollisionResult): boolean {
let distanceSquared = Vector2.distanceSquared(point, circle.position);
let distanceSquared = Vector2.sqrDistance(point, circle.position);
let sumOfRadii = 1 + circle.radius;
let collided = distanceSquared < sumOfRadii * sumOfRadii;
if (collided) {
result.normal = Vector2.normalize(Vector2.subtract(point, circle.position));
result.normal = point.sub(circle.position).normalize();
let depth = sumOfRadii - Math.sqrt(distanceSquared);
result.minimumTranslationVector = Vector2.multiplyScaler(result.normal, -depth);
result.point = Vector2.add(circle.position, Vector2.multiplyScaler(result.normal, circle.radius));
result.minimumTranslationVector = result.normal.scale(-depth);;
result.point = circle.position.add(result.normal.scale(circle.radius));
return true;
}
@@ -16,11 +16,11 @@ module es {
return false;
}
public static pointToBox(point: Vector2, box: Box, result: CollisionResult = new CollisionResult()){
if (box.containsPoint(point)){
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);
result.minimumTranslationVector = point.sub(result.point);
return true;
}
@@ -30,12 +30,15 @@ module es {
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);
const res = Polygon.getClosestPointOnPolygonToPoint(
poly.points,
point.sub(poly.position)
);
result.normal = res.edgeNormal;
result.minimumTranslationVector = result.normal.scale(
Math.sqrt(res.distanceSquared)
);
result.point = res.closestPoint.sub(poly.position);
return true;
}

View File

@@ -12,8 +12,8 @@ module es {
const firstEdges = first.edgeNormals;
const secondEdges = second.edgeNormals;
let minIntervalDistance = Number.POSITIVE_INFINITY;
let translationAxis = es.Vector2.zero;
let polygonOffset = Vector2.subtract(first.position, second.position);
let translationAxis = Vector2.zero;
let polygonOffset = first.position.sub(second.position);
let axis: Vector2;
// 循环穿过两个多边形的所有边
@@ -28,7 +28,7 @@ module es {
const {min: minB, max: maxB} = this.getInterval(axis, second);
// 将区间设为第二个多边形的空间。由轴上投影的位置差偏移。
let relativeIntervalOffset = Vector2.dot(polygonOffset, axis);
const relativeIntervalOffset = polygonOffset.dot(axis);
minA += relativeIntervalOffset;
maxA += relativeIntervalOffset;
@@ -50,14 +50,14 @@ module es {
minIntervalDistance = intervalDist;
translationAxis.setTo(axis.x, axis.y);
if (Vector2.dot(translationAxis, polygonOffset) < 0)
if (translationAxis.dot(polygonOffset) < 0)
translationAxis = translationAxis.scale(-1);
}
}
// 利用最小平移向量对多边形进行推入。
result.normal = translationAxis;
result.minimumTranslationVector = translationAxis.scale(minIntervalDistance * -1);
result.minimumTranslationVector = translationAxis.scale(-minIntervalDistance);
return true;
}
@@ -72,11 +72,11 @@ module es {
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);
dot = polygon.points[0].dot(axis);
res.max = dot;
res.min = dot;
for (let i = 1; i < polygon.points.length; i++) {
dot = Vector2.dot(polygon.points[i], axis);
dot = polygon.points[i].dot(axis);
if (dot < res.min) {
res.min = dot;
} else if (dot > res.max) {