新增扇形collider
This commit is contained in:
102
source/bin/framework.d.ts
vendored
102
source/bin/framework.d.ts
vendored
@@ -611,6 +611,11 @@ declare module es {
|
|||||||
* @returns 这个Vector2的平方长度
|
* @returns 这个Vector2的平方长度
|
||||||
*/
|
*/
|
||||||
lengthSquared(): number;
|
lengthSquared(): number;
|
||||||
|
/**
|
||||||
|
* 从原点到向量末端的距离
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getLength(): number;
|
||||||
/**
|
/**
|
||||||
* 四舍五入X和Y值
|
* 四舍五入X和Y值
|
||||||
*/
|
*/
|
||||||
@@ -621,6 +626,19 @@ declare module es {
|
|||||||
* @param right
|
* @param right
|
||||||
*/
|
*/
|
||||||
angleBetween(left: Vector2, right: Vector2): number;
|
angleBetween(left: Vector2, right: Vector2): number;
|
||||||
|
getDistance(other: Vector2): number;
|
||||||
|
getDistanceSquared(other: Vector2): number;
|
||||||
|
isBetween(v1: Vector2, v2: Vector2): boolean;
|
||||||
|
/**
|
||||||
|
* 两个向量的叉积
|
||||||
|
* @param other
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
cross(other: Vector2): number;
|
||||||
|
/**
|
||||||
|
* 计算向量与x轴之间的夹角
|
||||||
|
*/
|
||||||
|
getAngle(): number;
|
||||||
/**
|
/**
|
||||||
* 比较当前实例是否等于指定的对象
|
* 比较当前实例是否等于指定的对象
|
||||||
* @param other 要比较的对象
|
* @param other 要比较的对象
|
||||||
@@ -653,7 +671,9 @@ declare module es {
|
|||||||
*/
|
*/
|
||||||
static hermite(value1: Vector2, tangent1: Vector2, value2: Vector2, tangent2: Vector2, amount: number): Vector2;
|
static hermite(value1: Vector2, tangent1: Vector2, value2: Vector2, tangent2: Vector2, amount: number): Vector2;
|
||||||
static unsignedAngle(from: Vector2, to: Vector2, round?: boolean): number;
|
static unsignedAngle(from: Vector2, to: Vector2, round?: boolean): number;
|
||||||
|
static fromAngle(angle: number, magnitude?: number): Vector2;
|
||||||
clone(): Vector2;
|
clone(): Vector2;
|
||||||
|
copyFrom(source: Vector2): Vector2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
declare module es {
|
declare module es {
|
||||||
@@ -1548,6 +1568,14 @@ declare module es {
|
|||||||
constructor(points: Vector2[]);
|
constructor(points: Vector2[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
declare module es {
|
||||||
|
/**
|
||||||
|
* 扇形碰撞器
|
||||||
|
*/
|
||||||
|
class SectorCollider extends Collider {
|
||||||
|
constructor(center: Vector2, radius: number, startAngle: number, endAngle: number);
|
||||||
|
}
|
||||||
|
}
|
||||||
declare module es {
|
declare module es {
|
||||||
interface Map<K, V> {
|
interface Map<K, V> {
|
||||||
clear(): void;
|
clear(): void;
|
||||||
@@ -3714,10 +3742,12 @@ declare module es {
|
|||||||
* @param height
|
* @param height
|
||||||
*/
|
*/
|
||||||
updateBox(width: number, height: number): void;
|
updateBox(width: number, height: number): void;
|
||||||
|
getEdges(): Array<Line>;
|
||||||
overlaps(other: Shape): any;
|
overlaps(other: Shape): any;
|
||||||
collidesWithShape(other: Shape, result: Out<CollisionResult>): boolean;
|
collidesWithShape(other: Shape, result: Out<CollisionResult>): boolean;
|
||||||
containsPoint(point: Vector2): boolean;
|
containsPoint(point: Vector2): boolean;
|
||||||
pointCollidesWithShape(point: Vector2, result: Out<CollisionResult>): boolean;
|
pointCollidesWithShape(point: Vector2, result: Out<CollisionResult>): boolean;
|
||||||
|
getFurthestPoint(normal: Vector2): Vector2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
declare module es {
|
declare module es {
|
||||||
@@ -3767,6 +3797,34 @@ declare module es {
|
|||||||
toString(): string;
|
toString(): string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
declare module es {
|
||||||
|
class Line {
|
||||||
|
start: Vector2;
|
||||||
|
end: Vector2;
|
||||||
|
constructor(start: Vector2, end: Vector2);
|
||||||
|
readonly direction: Vector2;
|
||||||
|
getNormal(): Vector2;
|
||||||
|
getDirection(out: Vector2): Vector2;
|
||||||
|
getLength(): number;
|
||||||
|
getLengthSquared(): number;
|
||||||
|
distanceToPoint(normal: Vector2, center: Vector2): number;
|
||||||
|
getFurthestPoint(direction: Vector2): Vector2;
|
||||||
|
getClosestPoint(point: Vector2, out: Vector2): Vector2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
declare module es {
|
||||||
|
/**
|
||||||
|
* 计算投影和重叠区域
|
||||||
|
*/
|
||||||
|
class Projection {
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
constructor();
|
||||||
|
project(axis: Vector2, polygon: Polygon): void;
|
||||||
|
overlap(other: Projection): boolean;
|
||||||
|
getOverlap(other: Projection): number;
|
||||||
|
}
|
||||||
|
}
|
||||||
declare module es {
|
declare module es {
|
||||||
class RealtimeCollisions {
|
class RealtimeCollisions {
|
||||||
static intersectMovingCircleBox(s: Circle, b: Box, movement: Vector2, time: number): boolean;
|
static intersectMovingCircleBox(s: Circle, b: Box, movement: Vector2, time: number): boolean;
|
||||||
@@ -3785,6 +3843,50 @@ declare module es {
|
|||||||
static testCircleBox(cirlce: Circle, box: Box, point: Vector2): boolean;
|
static testCircleBox(cirlce: Circle, box: Box, point: Vector2): boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
declare module es {
|
||||||
|
/**
|
||||||
|
* 扇形形状
|
||||||
|
*/
|
||||||
|
class Sector extends Shape {
|
||||||
|
center: Vector2;
|
||||||
|
radius: number;
|
||||||
|
startAngle: number;
|
||||||
|
endAngle: number;
|
||||||
|
angle: number;
|
||||||
|
radiusSquared: number;
|
||||||
|
numberOfPoints: number;
|
||||||
|
angleStep: number;
|
||||||
|
points: Vector2[];
|
||||||
|
readonly sectorAngle: number;
|
||||||
|
constructor(center: Vector2, radius: number, startAngle: number, endAngle: number);
|
||||||
|
/**
|
||||||
|
* 扇形的圆心和半径计算出扇形的重心
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getCentroid(): Vector2;
|
||||||
|
/**
|
||||||
|
* 计算向量角度
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getAngle(): number;
|
||||||
|
recalculateBounds(collider: Collider): void;
|
||||||
|
overlaps(other: Shape): boolean;
|
||||||
|
collidesWithShape(other: Shape, collisionResult: Out<CollisionResult>): boolean;
|
||||||
|
collidesWithLine(start: Vector2, end: Vector2, hit: Out<RaycastHit>): boolean;
|
||||||
|
containsPoint(point: Vector2): boolean;
|
||||||
|
pointCollidesWithShape(point: Vector2, result: Out<CollisionResult>): boolean;
|
||||||
|
getPoints(): Vector2[];
|
||||||
|
private calculateProperties;
|
||||||
|
getFurthestPoint(normal: Vector2): Vector2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
declare module es {
|
||||||
|
class ShapeCollisionSector {
|
||||||
|
static sectorToPolygon(first: Sector, second: Polygon, result: Out<CollisionResult>): boolean;
|
||||||
|
static sectorToCircle(first: Sector, second: Circle, result: Out<CollisionResult>): boolean;
|
||||||
|
static sectorToBox(first: Sector, second: Box, result: Out<CollisionResult>): boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
declare module es {
|
declare module es {
|
||||||
class ShapeCollisionsBox {
|
class ShapeCollisionsBox {
|
||||||
static boxToBox(first: Box, second: Box, result: Out<CollisionResult>): boolean;
|
static boxToBox(first: Box, second: Box, result: Out<CollisionResult>): boolean;
|
||||||
|
|||||||
@@ -1478,6 +1478,13 @@ var es;
|
|||||||
Vector2.prototype.lengthSquared = function () {
|
Vector2.prototype.lengthSquared = function () {
|
||||||
return (this.x * this.x) + (this.y * this.y);
|
return (this.x * this.x) + (this.y * this.y);
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 从原点到向量末端的距离
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
Vector2.prototype.getLength = function () {
|
||||||
|
return Math.sqrt(this.x * this.x + this.y * this.y);
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* 四舍五入X和Y值
|
* 四舍五入X和Y值
|
||||||
*/
|
*/
|
||||||
@@ -1494,6 +1501,32 @@ var es;
|
|||||||
var two = right.sub(this);
|
var two = right.sub(this);
|
||||||
return es.Vector2Ext.angle(one, two);
|
return es.Vector2Ext.angle(one, two);
|
||||||
};
|
};
|
||||||
|
Vector2.prototype.getDistance = function (other) {
|
||||||
|
return Math.sqrt(this.getDistanceSquared(other));
|
||||||
|
};
|
||||||
|
Vector2.prototype.getDistanceSquared = function (other) {
|
||||||
|
var dx = other.x - this.x;
|
||||||
|
var dy = other.y - this.y;
|
||||||
|
return dx * dx + dy * dy;
|
||||||
|
};
|
||||||
|
Vector2.prototype.isBetween = function (v1, v2) {
|
||||||
|
var cross = v2.sub(v1).cross(this.sub(v1));
|
||||||
|
return Math.abs(cross) < Number.EPSILON && this.dot(v2.sub(v1)) >= 0 && this.dot(v1.sub(v2)) >= 0;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 两个向量的叉积
|
||||||
|
* @param other
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
Vector2.prototype.cross = function (other) {
|
||||||
|
return this.x * other.y - this.y * other.x;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 计算向量与x轴之间的夹角
|
||||||
|
*/
|
||||||
|
Vector2.prototype.getAngle = function () {
|
||||||
|
return Math.atan2(this.y, this.x);
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* 比较当前实例是否等于指定的对象
|
* 比较当前实例是否等于指定的对象
|
||||||
* @param other 要比较的对象
|
* @param other 要比较的对象
|
||||||
@@ -1543,9 +1576,18 @@ var es;
|
|||||||
var angle = Math.acos(es.MathHelper.clamp(from.dot(to), -1, 1)) * es.MathHelper.Rad2Deg;
|
var angle = Math.acos(es.MathHelper.clamp(from.dot(to), -1, 1)) * es.MathHelper.Rad2Deg;
|
||||||
return round ? Math.round(angle) : angle;
|
return round ? Math.round(angle) : angle;
|
||||||
};
|
};
|
||||||
|
Vector2.fromAngle = function (angle, magnitude) {
|
||||||
|
if (magnitude === void 0) { magnitude = 1; }
|
||||||
|
return new Vector2(magnitude * Math.cos(angle), magnitude * Math.sin(angle));
|
||||||
|
};
|
||||||
Vector2.prototype.clone = function () {
|
Vector2.prototype.clone = function () {
|
||||||
return new Vector2(this.x, this.y);
|
return new Vector2(this.x, this.y);
|
||||||
};
|
};
|
||||||
|
Vector2.prototype.copyFrom = function (source) {
|
||||||
|
this.x = source.x;
|
||||||
|
this.y = source.y;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
return Vector2;
|
return Vector2;
|
||||||
}());
|
}());
|
||||||
es.Vector2 = Vector2;
|
es.Vector2 = Vector2;
|
||||||
@@ -3728,6 +3770,22 @@ var es;
|
|||||||
es.PolygonCollider = PolygonCollider;
|
es.PolygonCollider = PolygonCollider;
|
||||||
})(es || (es = {}));
|
})(es || (es = {}));
|
||||||
var es;
|
var es;
|
||||||
|
(function (es) {
|
||||||
|
/**
|
||||||
|
* 扇形碰撞器
|
||||||
|
*/
|
||||||
|
var SectorCollider = /** @class */ (function (_super) {
|
||||||
|
__extends(SectorCollider, _super);
|
||||||
|
function SectorCollider(center, radius, startAngle, endAngle) {
|
||||||
|
var _this = _super.call(this) || this;
|
||||||
|
_this.shape = new es.Sector(center, radius, startAngle, endAngle);
|
||||||
|
return _this;
|
||||||
|
}
|
||||||
|
return SectorCollider;
|
||||||
|
}(es.Collider));
|
||||||
|
es.SectorCollider = SectorCollider;
|
||||||
|
})(es || (es = {}));
|
||||||
|
var es;
|
||||||
(function (es) {
|
(function (es) {
|
||||||
function decode(key) {
|
function decode(key) {
|
||||||
switch (typeof key) {
|
switch (typeof key) {
|
||||||
@@ -9168,6 +9226,14 @@ var es;
|
|||||||
for (var i = 0; i < this.points.length; i++)
|
for (var i = 0; i < this.points.length; i++)
|
||||||
this._originalPoints[i] = this.points[i];
|
this._originalPoints[i] = this.points[i];
|
||||||
};
|
};
|
||||||
|
Box.prototype.getEdges = function () {
|
||||||
|
var edges = [];
|
||||||
|
for (var i = 0; i < this.points.length; i++) {
|
||||||
|
var j = (i + 1) % this.points.length;
|
||||||
|
edges.push(new es.Line(this.points[i], this.points[j]));
|
||||||
|
}
|
||||||
|
return edges;
|
||||||
|
};
|
||||||
Box.prototype.overlaps = function (other) {
|
Box.prototype.overlaps = function (other) {
|
||||||
// 特殊情况,这一个高性能方式实现,其他情况则使用polygon方法检测
|
// 特殊情况,这一个高性能方式实现,其他情况则使用polygon方法检测
|
||||||
if (this.isUnrotated) {
|
if (this.isUnrotated) {
|
||||||
@@ -9196,6 +9262,29 @@ var es;
|
|||||||
return es.ShapeCollisionsPoint.pointToBox(point, this, result);
|
return es.ShapeCollisionsPoint.pointToBox(point, this, result);
|
||||||
return _super.prototype.pointCollidesWithShape.call(this, point, result);
|
return _super.prototype.pointCollidesWithShape.call(this, point, result);
|
||||||
};
|
};
|
||||||
|
Box.prototype.getFurthestPoint = function (normal) {
|
||||||
|
var furthestPoint = new es.Vector2(this.width / 2, this.height / 2);
|
||||||
|
var dotProduct = furthestPoint.dot(normal);
|
||||||
|
var tempPoint = new es.Vector2(-this.width / 2, this.height / 2);
|
||||||
|
var tempDotProduct = tempPoint.dot(normal);
|
||||||
|
if (tempDotProduct > dotProduct) {
|
||||||
|
furthestPoint.copyFrom(tempPoint);
|
||||||
|
dotProduct = tempDotProduct;
|
||||||
|
}
|
||||||
|
tempPoint.setTo(-this.width / 2, -this.height / 2);
|
||||||
|
tempDotProduct = tempPoint.dot(normal);
|
||||||
|
if (tempDotProduct > dotProduct) {
|
||||||
|
furthestPoint.copyFrom(tempPoint);
|
||||||
|
dotProduct = tempDotProduct;
|
||||||
|
}
|
||||||
|
tempPoint.setTo(this.width / 2, -this.height / 2);
|
||||||
|
tempDotProduct = tempPoint.dot(normal);
|
||||||
|
if (tempDotProduct > dotProduct) {
|
||||||
|
furthestPoint.copyFrom(tempPoint);
|
||||||
|
dotProduct = tempDotProduct;
|
||||||
|
}
|
||||||
|
return furthestPoint;
|
||||||
|
};
|
||||||
return Box;
|
return Box;
|
||||||
}(es.Polygon));
|
}(es.Polygon));
|
||||||
es.Box = Box;
|
es.Box = Box;
|
||||||
@@ -9336,6 +9425,93 @@ var es;
|
|||||||
es.CollisionResult = CollisionResult;
|
es.CollisionResult = CollisionResult;
|
||||||
})(es || (es = {}));
|
})(es || (es = {}));
|
||||||
var es;
|
var es;
|
||||||
|
(function (es) {
|
||||||
|
var Line = /** @class */ (function () {
|
||||||
|
function Line(start, end) {
|
||||||
|
this.start = start.clone();
|
||||||
|
this.end = end.clone();
|
||||||
|
}
|
||||||
|
Object.defineProperty(Line.prototype, "direction", {
|
||||||
|
get: function () {
|
||||||
|
return this.end.sub(this.start).normalize();
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
Line.prototype.getNormal = function () {
|
||||||
|
var angle = this.direction.getAngle() - Math.PI / 2;
|
||||||
|
return new es.Vector2(Math.cos(angle), Math.sin(angle));
|
||||||
|
};
|
||||||
|
Line.prototype.getDirection = function (out) {
|
||||||
|
return out.copyFrom(this.end).sub(this.start).normalize();
|
||||||
|
};
|
||||||
|
Line.prototype.getLength = function () {
|
||||||
|
return this.start.getDistance(this.end);
|
||||||
|
};
|
||||||
|
Line.prototype.getLengthSquared = function () {
|
||||||
|
return this.start.getDistanceSquared(this.end);
|
||||||
|
};
|
||||||
|
Line.prototype.distanceToPoint = function (normal, center) {
|
||||||
|
return Math.abs((this.end.y - this.start.y) * normal.x - (this.end.x - this.start.x) * normal.y + this.end.x * this.start.y - this.end.y * this.start.x) / (2 * normal.magnitude());
|
||||||
|
};
|
||||||
|
Line.prototype.getFurthestPoint = function (direction) {
|
||||||
|
var d1 = this.start.dot(direction);
|
||||||
|
var d2 = this.end.dot(direction);
|
||||||
|
return d1 > d2 ? this.start : this.end;
|
||||||
|
};
|
||||||
|
Line.prototype.getClosestPoint = function (point, out) {
|
||||||
|
var delta = out.copyFrom(this.end).sub(this.start);
|
||||||
|
var t = (point.sub(this.start)).dot(delta) / delta.lengthSquared();
|
||||||
|
if (t < 0) {
|
||||||
|
return out.copyFrom(this.start);
|
||||||
|
}
|
||||||
|
else if (t > 1) {
|
||||||
|
return out.copyFrom(this.end);
|
||||||
|
}
|
||||||
|
return out.copyFrom(delta).multiplyScaler(t).add(this.start);
|
||||||
|
};
|
||||||
|
return Line;
|
||||||
|
}());
|
||||||
|
es.Line = Line;
|
||||||
|
})(es || (es = {}));
|
||||||
|
var es;
|
||||||
|
(function (es) {
|
||||||
|
/**
|
||||||
|
* 计算投影和重叠区域
|
||||||
|
*/
|
||||||
|
var Projection = /** @class */ (function () {
|
||||||
|
function Projection() {
|
||||||
|
this.min = Number.MAX_VALUE;
|
||||||
|
this.max = -Number.MAX_VALUE;
|
||||||
|
}
|
||||||
|
Projection.prototype.project = function (axis, polygon) {
|
||||||
|
var points = polygon.points;
|
||||||
|
var min = axis.dot(points[0]);
|
||||||
|
var max = min;
|
||||||
|
for (var i = 1; i < points.length; i++) {
|
||||||
|
var p = points[i];
|
||||||
|
var dot = axis.dot(p);
|
||||||
|
if (dot < min) {
|
||||||
|
min = dot;
|
||||||
|
}
|
||||||
|
else if (dot > max) {
|
||||||
|
max = dot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
};
|
||||||
|
Projection.prototype.overlap = function (other) {
|
||||||
|
return this.max >= other.min && other.max >= this.min;
|
||||||
|
};
|
||||||
|
Projection.prototype.getOverlap = function (other) {
|
||||||
|
return Math.min(this.max, other.max) - Math.max(this.min, other.min);
|
||||||
|
};
|
||||||
|
return Projection;
|
||||||
|
}());
|
||||||
|
es.Projection = Projection;
|
||||||
|
})(es || (es = {}));
|
||||||
|
var es;
|
||||||
(function (es) {
|
(function (es) {
|
||||||
var RealtimeCollisions = /** @class */ (function () {
|
var RealtimeCollisions = /** @class */ (function () {
|
||||||
function RealtimeCollisions() {
|
function RealtimeCollisions() {
|
||||||
@@ -9406,6 +9582,270 @@ var es;
|
|||||||
es.RealtimeCollisions = RealtimeCollisions;
|
es.RealtimeCollisions = RealtimeCollisions;
|
||||||
})(es || (es = {}));
|
})(es || (es = {}));
|
||||||
var es;
|
var es;
|
||||||
|
(function (es) {
|
||||||
|
/**
|
||||||
|
* 扇形形状
|
||||||
|
*/
|
||||||
|
var Sector = /** @class */ (function (_super) {
|
||||||
|
__extends(Sector, _super);
|
||||||
|
function Sector(center, radius, startAngle, endAngle) {
|
||||||
|
var _this = _super.call(this) || this;
|
||||||
|
_this.center = center;
|
||||||
|
_this.radius = radius;
|
||||||
|
_this.startAngle = startAngle;
|
||||||
|
_this.endAngle = endAngle;
|
||||||
|
_this.angle = endAngle - startAngle;
|
||||||
|
_this.radiusSquared = radius * radius;
|
||||||
|
_this.points = _this.getPoints();
|
||||||
|
_this.calculateProperties();
|
||||||
|
return _this;
|
||||||
|
}
|
||||||
|
Object.defineProperty(Sector.prototype, "sectorAngle", {
|
||||||
|
get: function () {
|
||||||
|
var angle = this.endAngle - this.startAngle;
|
||||||
|
if (angle < 0)
|
||||||
|
angle += 360;
|
||||||
|
return angle;
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* 扇形的圆心和半径计算出扇形的重心
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
Sector.prototype.getCentroid = function () {
|
||||||
|
var x = (Math.cos(this.startAngle) + Math.cos(this.endAngle)) * this.radius / 3;
|
||||||
|
var y = (Math.sin(this.startAngle) + Math.sin(this.endAngle)) * this.radius / 3;
|
||||||
|
return new es.Vector2(x + this.center.x, y + this.center.y);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 计算向量角度
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
Sector.prototype.getAngle = function () {
|
||||||
|
return this.startAngle;
|
||||||
|
};
|
||||||
|
Sector.prototype.recalculateBounds = function (collider) {
|
||||||
|
var localCenter = this.center.add(collider.localOffset);
|
||||||
|
var x = localCenter.x - this.radius;
|
||||||
|
var y = localCenter.y - this.radius;
|
||||||
|
var width = this.radius * 2;
|
||||||
|
var height = this.radius * 2;
|
||||||
|
var bounds = new es.Rectangle(x, y, width, height);
|
||||||
|
this.bounds = bounds;
|
||||||
|
this.center = localCenter;
|
||||||
|
};
|
||||||
|
Sector.prototype.overlaps = function (other) {
|
||||||
|
var result = new es.Out();
|
||||||
|
if (other instanceof es.Polygon)
|
||||||
|
return es.ShapeCollisionSector.sectorToPolygon(this, other, result);
|
||||||
|
if (other instanceof es.Circle) {
|
||||||
|
if (es.ShapeCollisionSector.sectorToCircle(this, other, result)) {
|
||||||
|
result.value.invertResult();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw new Error("overlaps of Sector to " + other + " are not supported");
|
||||||
|
};
|
||||||
|
Sector.prototype.collidesWithShape = function (other, collisionResult) {
|
||||||
|
if (other instanceof es.Box) {
|
||||||
|
return es.ShapeCollisionSector.sectorToBox(this, other, collisionResult);
|
||||||
|
}
|
||||||
|
if (other instanceof es.Polygon) {
|
||||||
|
return es.ShapeCollisionSector.sectorToPolygon(this, other, collisionResult);
|
||||||
|
}
|
||||||
|
if (other instanceof es.Circle) {
|
||||||
|
return es.ShapeCollisionSector.sectorToCircle(this, other, collisionResult);
|
||||||
|
}
|
||||||
|
throw new Error("overlaps of Polygon to " + other + " are not supported");
|
||||||
|
};
|
||||||
|
Sector.prototype.collidesWithLine = function (start, end, hit) {
|
||||||
|
var toStart = start.sub(this.center);
|
||||||
|
var toEnd = end.sub(this.center);
|
||||||
|
var angleStart = toStart.getAngle();
|
||||||
|
var angleEnd = toEnd.getAngle();
|
||||||
|
var angleDiff = angleEnd - angleStart;
|
||||||
|
if (angleDiff > Math.PI) {
|
||||||
|
angleDiff -= 2 * Math.PI;
|
||||||
|
}
|
||||||
|
else if (angleDiff < -Math.PI) {
|
||||||
|
angleDiff += 2 * Math.PI;
|
||||||
|
}
|
||||||
|
if (angleDiff >= this.startAngle && angleDiff <= this.endAngle) {
|
||||||
|
var r = toStart.getLength();
|
||||||
|
var t = this.startAngle - angleStart;
|
||||||
|
var x = r * Math.cos(t);
|
||||||
|
var y = r * Math.sin(t);
|
||||||
|
var intersection = new es.Vector2(x, y);
|
||||||
|
if (intersection.isBetween(start, end)) {
|
||||||
|
var distance = intersection.sub(start).getLength();
|
||||||
|
var fraction = distance / start.getDistance(end);
|
||||||
|
var normal = intersection.sub(this.center).normalize();
|
||||||
|
var point = intersection.add(this.center);
|
||||||
|
var raycastHit = new es.RaycastHit();
|
||||||
|
raycastHit.setValues(fraction, distance, point, normal);
|
||||||
|
hit.value = raycastHit;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
Sector.prototype.containsPoint = function (point) {
|
||||||
|
var toPoint = point.sub(this.center);
|
||||||
|
var distanceSquared = toPoint.lengthSquared();
|
||||||
|
if (distanceSquared > this.radiusSquared) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var angle = toPoint.getAngle();
|
||||||
|
var startAngle = this.startAngle;
|
||||||
|
var endAngle = startAngle + this.angle;
|
||||||
|
var angleDiff = angle - startAngle;
|
||||||
|
if (angleDiff < 0) {
|
||||||
|
angleDiff += Math.PI * 2;
|
||||||
|
}
|
||||||
|
if (angleDiff > this.angle) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
Sector.prototype.pointCollidesWithShape = function (point, result) {
|
||||||
|
if (!this.containsPoint(point)) {
|
||||||
|
if (result) {
|
||||||
|
result.value = null;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
result.value = new es.CollisionResult();
|
||||||
|
result.value.normal = point.sub(this.center).normalize();
|
||||||
|
result.value.minimumTranslationVector = result.value.normal.scale(this.radius - point.sub(this.center).getLength());
|
||||||
|
result.value.point = point;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
Sector.prototype.getPoints = function () {
|
||||||
|
var points = new Array(this.numberOfPoints);
|
||||||
|
for (var i = 0; i < this.numberOfPoints; i++) {
|
||||||
|
var angle = this.startAngle + i * this.angleStep;
|
||||||
|
points[i] = es.Vector2.fromAngle(angle, this.radius).add(this.center);
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
};
|
||||||
|
Sector.prototype.calculateProperties = function () {
|
||||||
|
this.numberOfPoints = Math.max(10, Math.floor(this.radius * 0.1));
|
||||||
|
this.angleStep = (this.endAngle - this.startAngle) / (this.numberOfPoints - 1);
|
||||||
|
};
|
||||||
|
Sector.prototype.getFurthestPoint = function (normal) {
|
||||||
|
var maxProjection = -Number.MAX_VALUE;
|
||||||
|
var furthestPoint = new es.Vector2();
|
||||||
|
for (var i = 0; i < this.numberOfPoints; i++) {
|
||||||
|
var projection = this.points[i].dot(normal);
|
||||||
|
if (projection > maxProjection) {
|
||||||
|
maxProjection = projection;
|
||||||
|
furthestPoint.copyFrom(this.points[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return furthestPoint;
|
||||||
|
};
|
||||||
|
return Sector;
|
||||||
|
}(es.Shape));
|
||||||
|
es.Sector = Sector;
|
||||||
|
})(es || (es = {}));
|
||||||
|
var es;
|
||||||
|
(function (es) {
|
||||||
|
var ShapeCollisionSector = /** @class */ (function () {
|
||||||
|
function ShapeCollisionSector() {
|
||||||
|
}
|
||||||
|
ShapeCollisionSector.sectorToPolygon = function (first, second, result) {
|
||||||
|
var numPoints = second.points.length;
|
||||||
|
var collision = false;
|
||||||
|
var edgeStart = new es.Vector2();
|
||||||
|
var edgeEnd = new es.Vector2();
|
||||||
|
var hit = new es.Out();
|
||||||
|
for (var i = 0; i < numPoints; i++) {
|
||||||
|
var point = second.points[i];
|
||||||
|
if (first.containsPoint(point)) {
|
||||||
|
if (result) {
|
||||||
|
result.value = new es.CollisionResult();
|
||||||
|
result.value.point = point.clone();
|
||||||
|
result.value.normal = point.sub(first.center).normalize();
|
||||||
|
}
|
||||||
|
collision = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!collision && second.containsPoint(first.center)) {
|
||||||
|
if (result) {
|
||||||
|
result.value = new es.CollisionResult();
|
||||||
|
result.value.point = first.center.clone();
|
||||||
|
result.value.normal = new es.Vector2(0, 0);
|
||||||
|
}
|
||||||
|
collision = true;
|
||||||
|
}
|
||||||
|
if (!collision) {
|
||||||
|
for (var i = 0; i < numPoints; i++) {
|
||||||
|
var p1 = second.points[i];
|
||||||
|
var p2 = second.points[(i + 1) % numPoints];
|
||||||
|
edgeStart.copyFrom(p1);
|
||||||
|
edgeEnd.copyFrom(p2);
|
||||||
|
if (first.collidesWithLine(edgeStart, edgeEnd, hit)) {
|
||||||
|
if (result) {
|
||||||
|
result.value = new es.CollisionResult();
|
||||||
|
result.value.point.copyFrom(hit.value.point);
|
||||||
|
result.value.normal.copyFrom(hit.value.normal);
|
||||||
|
}
|
||||||
|
collision = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return collision;
|
||||||
|
};
|
||||||
|
ShapeCollisionSector.sectorToCircle = function (first, second, result) {
|
||||||
|
var radiusSquared = second.radius * second.radius;
|
||||||
|
var distanceSquared = first.center.getDistanceSquared(second.center);
|
||||||
|
var angleDiff = Math.abs(second.center.sub(first.center).getAngle() - first.getAngle());
|
||||||
|
var sectorAngle = first.endAngle - first.startAngle;
|
||||||
|
if (distanceSquared <= radiusSquared && angleDiff <= sectorAngle / 2) {
|
||||||
|
if (result) {
|
||||||
|
result.value = new es.CollisionResult();
|
||||||
|
result.value.normal = second.center.sub(first.center).normalize();
|
||||||
|
result.value.point = second.center.clone().add(result.value.normal.clone().multiplyScaler(second.radius));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
result.value = null;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
ShapeCollisionSector.sectorToBox = function (first, second, result) {
|
||||||
|
result.value = new es.CollisionResult();
|
||||||
|
// 获取box的四条边
|
||||||
|
var boxEdges = second.getEdges();
|
||||||
|
// 遍历box的每一条边
|
||||||
|
for (var i = 0; i < boxEdges.length; i++) {
|
||||||
|
var normal = boxEdges[i].getNormal();
|
||||||
|
var furthestPointBox = second.getFurthestPoint(normal);
|
||||||
|
var furthestPointSector = first.getFurthestPoint(normal.negate());
|
||||||
|
var distance = normal.dot(furthestPointSector.sub(furthestPointBox));
|
||||||
|
// 没有相交
|
||||||
|
if (distance > 0)
|
||||||
|
return false;
|
||||||
|
if (result.value && Math.abs(distance) < result.value.minimumTranslationVector.getLength()) {
|
||||||
|
result.value.minimumTranslationVector = normal.clone().multiplyScaler(distance);
|
||||||
|
result.value.normal = normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return ShapeCollisionSector;
|
||||||
|
}());
|
||||||
|
es.ShapeCollisionSector = ShapeCollisionSector;
|
||||||
|
})(es || (es = {}));
|
||||||
|
var es;
|
||||||
(function (es) {
|
(function (es) {
|
||||||
var ShapeCollisionsBox = /** @class */ (function () {
|
var ShapeCollisionsBox = /** @class */ (function () {
|
||||||
function ShapeCollisionsBox() {
|
function ShapeCollisionsBox() {
|
||||||
|
|||||||
2
source/bin/framework.min.js
vendored
2
source/bin/framework.min.js
vendored
File diff suppressed because one or more lines are too long
7311
source/package-lock.json
generated
7311
source/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
|||||||
|
module es {
|
||||||
|
/**
|
||||||
|
* 扇形碰撞器
|
||||||
|
*/
|
||||||
|
export class SectorCollider extends Collider {
|
||||||
|
constructor(
|
||||||
|
center: Vector2,
|
||||||
|
radius: number,
|
||||||
|
startAngle: number,
|
||||||
|
endAngle: number
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this.shape = new Sector(center, radius, startAngle, endAngle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -337,6 +337,14 @@ module es {
|
|||||||
return (this.x * this.x) + (this.y * this.y);
|
return (this.x * this.x) + (this.y * this.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从原点到向量末端的距离
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public getLength(): number {
|
||||||
|
return Math.sqrt(this.x * this.x + this.y * this.y);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 四舍五入X和Y值
|
* 四舍五入X和Y值
|
||||||
*/
|
*/
|
||||||
@@ -355,6 +363,37 @@ module es {
|
|||||||
return Vector2Ext.angle(one, two);
|
return Vector2Ext.angle(one, two);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDistance(other: Vector2): number {
|
||||||
|
return Math.sqrt(this.getDistanceSquared(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDistanceSquared(other: Vector2): number {
|
||||||
|
const dx = other.x - this.x;
|
||||||
|
const dy = other.y - this.y;
|
||||||
|
return dx * dx + dy * dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isBetween(v1: Vector2, v2: Vector2): boolean {
|
||||||
|
const cross = v2.sub(v1).cross(this.sub(v1));
|
||||||
|
return Math.abs(cross) < Number.EPSILON && this.dot(v2.sub(v1)) >= 0 && this.dot(v1.sub(v2)) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 两个向量的叉积
|
||||||
|
* @param other
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public cross(other: Vector2): number {
|
||||||
|
return this.x * other.y - this.y * other.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算向量与x轴之间的夹角
|
||||||
|
*/
|
||||||
|
public getAngle(): number {
|
||||||
|
return Math.atan2(this.y, this.x);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 比较当前实例是否等于指定的对象
|
* 比较当前实例是否等于指定的对象
|
||||||
* @param other 要比较的对象
|
* @param other 要比较的对象
|
||||||
@@ -412,9 +451,19 @@ module es {
|
|||||||
return round ? Math.round(angle) : angle;
|
return round ? Math.round(angle) : angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static fromAngle(angle: number, magnitude: number = 1): Vector2 {
|
||||||
|
return new Vector2(magnitude * Math.cos(angle), magnitude * Math.sin(angle));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public clone(): Vector2 {
|
public clone(): Vector2 {
|
||||||
return new Vector2(this.x, this.y);
|
return new Vector2(this.x, this.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public copyFrom(source: Vector2): Vector2 {
|
||||||
|
this.x = source.x;
|
||||||
|
this.y = source.y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,17 @@ module es {
|
|||||||
this._originalPoints[i] = this.points[i];
|
this._originalPoints[i] = this.points[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getEdges(): Array<Line> {
|
||||||
|
const edges = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.points.length; i++) {
|
||||||
|
const j = (i + 1) % this.points.length;
|
||||||
|
edges.push(new Line(this.points[i], this.points[j]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return edges;
|
||||||
|
}
|
||||||
|
|
||||||
public overlaps(other: Shape) {
|
public overlaps(other: Shape) {
|
||||||
// 特殊情况,这一个高性能方式实现,其他情况则使用polygon方法检测
|
// 特殊情况,这一个高性能方式实现,其他情况则使用polygon方法检测
|
||||||
if (this.isUnrotated) {
|
if (this.isUnrotated) {
|
||||||
@@ -90,5 +101,33 @@ module es {
|
|||||||
|
|
||||||
return super.pointCollidesWithShape(point, result);
|
return super.pointCollidesWithShape(point, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getFurthestPoint(normal: Vector2): Vector2 {
|
||||||
|
let furthestPoint = new Vector2(this.width / 2, this.height / 2);
|
||||||
|
let dotProduct = furthestPoint.dot(normal);
|
||||||
|
|
||||||
|
let tempPoint = new Vector2(-this.width / 2, this.height / 2);
|
||||||
|
let tempDotProduct = tempPoint.dot(normal);
|
||||||
|
if (tempDotProduct > dotProduct) {
|
||||||
|
furthestPoint.copyFrom(tempPoint);
|
||||||
|
dotProduct = tempDotProduct;
|
||||||
|
}
|
||||||
|
|
||||||
|
tempPoint.setTo(-this.width / 2, -this.height / 2);
|
||||||
|
tempDotProduct = tempPoint.dot(normal);
|
||||||
|
if (tempDotProduct > dotProduct) {
|
||||||
|
furthestPoint.copyFrom(tempPoint);
|
||||||
|
dotProduct = tempDotProduct;
|
||||||
|
}
|
||||||
|
|
||||||
|
tempPoint.setTo(this.width / 2, -this.height / 2);
|
||||||
|
tempDotProduct = tempPoint.dot(normal);
|
||||||
|
if (tempDotProduct > dotProduct) {
|
||||||
|
furthestPoint.copyFrom(tempPoint);
|
||||||
|
dotProduct = tempDotProduct;
|
||||||
|
}
|
||||||
|
|
||||||
|
return furthestPoint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
55
source/src/Physics/Shapes/Line.ts
Normal file
55
source/src/Physics/Shapes/Line.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
module es {
|
||||||
|
export class Line {
|
||||||
|
public start: Vector2;
|
||||||
|
public end: Vector2;
|
||||||
|
|
||||||
|
constructor(start: Vector2, end: Vector2) {
|
||||||
|
this.start = start.clone();
|
||||||
|
this.end = end.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get direction(): Vector2 {
|
||||||
|
return this.end.sub(this.start).normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNormal(): Vector2 {
|
||||||
|
const angle = this.direction.getAngle() - Math.PI / 2;
|
||||||
|
return new Vector2(Math.cos(angle), Math.sin(angle));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDirection(out: Vector2) {
|
||||||
|
return out.copyFrom(this.end).sub(this.start).normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLength() {
|
||||||
|
return this.start.getDistance(this.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLengthSquared() {
|
||||||
|
return this.start.getDistanceSquared(this.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public distanceToPoint(normal: Vector2, center: Vector2): number {
|
||||||
|
return Math.abs((this.end.y - this.start.y) * normal.x - (this.end.x - this.start.x) * normal.y + this.end.x * this.start.y - this.end.y * this.start.x) / (2 * normal.magnitude());
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFurthestPoint(direction: Vector2): Vector2 {
|
||||||
|
const d1 = this.start.dot(direction);
|
||||||
|
const d2 = this.end.dot(direction);
|
||||||
|
return d1 > d2 ? this.start : this.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getClosestPoint(point: Vector2, out: Vector2) {
|
||||||
|
const delta = out.copyFrom(this.end).sub(this.start);
|
||||||
|
const t = (point.sub(this.start)).dot(delta) / delta.lengthSquared();
|
||||||
|
|
||||||
|
if (t < 0) {
|
||||||
|
return out.copyFrom(this.start);
|
||||||
|
} else if (t > 1) {
|
||||||
|
return out.copyFrom(this.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.copyFrom(delta).multiplyScaler(t).add(this.start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
source/src/Physics/Shapes/Projection.ts
Normal file
41
source/src/Physics/Shapes/Projection.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
module es {
|
||||||
|
/**
|
||||||
|
* 计算投影和重叠区域
|
||||||
|
*/
|
||||||
|
export class Projection {
|
||||||
|
public min: number;
|
||||||
|
public max: number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.min = Number.MAX_VALUE;
|
||||||
|
this.max = -Number.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public project(axis: Vector2, polygon: Polygon) {
|
||||||
|
const points = polygon.points;
|
||||||
|
let min = axis.dot(points[0]);
|
||||||
|
let max = min;
|
||||||
|
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
const p = points[i];
|
||||||
|
const dot = axis.dot(p);
|
||||||
|
if (dot < min) {
|
||||||
|
min = dot;
|
||||||
|
} else if (dot > max) {
|
||||||
|
max = dot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public overlap(other: Projection): boolean {
|
||||||
|
return this.max >= other.min && other.max >= this.min;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getOverlap(other: Projection): number {
|
||||||
|
return Math.min(this.max, other.max) - Math.max(this.min, other.min);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
205
source/src/Physics/Shapes/Sector.ts
Normal file
205
source/src/Physics/Shapes/Sector.ts
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
module es {
|
||||||
|
/**
|
||||||
|
* 扇形形状
|
||||||
|
*/
|
||||||
|
export class Sector extends Shape {
|
||||||
|
public center: Vector2;
|
||||||
|
public radius: number;
|
||||||
|
public startAngle: number;
|
||||||
|
public endAngle: number;
|
||||||
|
public angle: number;
|
||||||
|
public radiusSquared: number;
|
||||||
|
public numberOfPoints: number;
|
||||||
|
public angleStep: number;
|
||||||
|
public points: Vector2[];
|
||||||
|
|
||||||
|
public get sectorAngle(): number {
|
||||||
|
let angle = this.endAngle - this.startAngle;
|
||||||
|
if (angle < 0) angle += 360;
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(center: Vector2, radius: number, startAngle: number, endAngle: number) {
|
||||||
|
super();
|
||||||
|
this.center = center;
|
||||||
|
this.radius = radius;
|
||||||
|
this.startAngle = startAngle;
|
||||||
|
this.endAngle = endAngle;
|
||||||
|
this.angle = endAngle - startAngle;
|
||||||
|
this.radiusSquared = radius * radius;
|
||||||
|
this.points = this.getPoints();
|
||||||
|
this.calculateProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扇形的圆心和半径计算出扇形的重心
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public getCentroid(): Vector2 {
|
||||||
|
const x = (Math.cos(this.startAngle) + Math.cos(this.endAngle)) * this.radius / 3;
|
||||||
|
const y = (Math.sin(this.startAngle) + Math.sin(this.endAngle)) * this.radius / 3;
|
||||||
|
return new Vector2(x + this.center.x, y + this.center.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算向量角度
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public getAngle(): number {
|
||||||
|
return this.startAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public recalculateBounds(collider: Collider): void {
|
||||||
|
const localCenter = this.center.add(collider.localOffset);
|
||||||
|
const x = localCenter.x - this.radius;
|
||||||
|
const y = localCenter.y - this.radius;
|
||||||
|
const width = this.radius * 2;
|
||||||
|
const height = this.radius * 2;
|
||||||
|
const bounds = new Rectangle(x, y, width, height);
|
||||||
|
this.bounds = bounds;
|
||||||
|
|
||||||
|
this.center = localCenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public overlaps(other: Shape): boolean {
|
||||||
|
let result = new Out<CollisionResult>();
|
||||||
|
if (other instanceof Polygon)
|
||||||
|
return ShapeCollisionSector.sectorToPolygon(this, other, result);
|
||||||
|
|
||||||
|
if (other instanceof Circle) {
|
||||||
|
if (ShapeCollisionSector.sectorToCircle(this, other, result)) {
|
||||||
|
result.value.invertResult();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`overlaps of Sector to ${other} are not supported`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public collidesWithShape(other: Shape, collisionResult: Out<CollisionResult>): boolean {
|
||||||
|
if (other instanceof Box) {
|
||||||
|
return ShapeCollisionSector.sectorToBox(this, other, collisionResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other instanceof Polygon) {
|
||||||
|
return ShapeCollisionSector.sectorToPolygon(this, other, collisionResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other instanceof Circle) {
|
||||||
|
return ShapeCollisionSector.sectorToCircle(this, other, collisionResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`overlaps of Polygon to ${other} are not supported`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public collidesWithLine(start: Vector2, end: Vector2, hit: Out<RaycastHit>): boolean {
|
||||||
|
const toStart = start.sub(this.center);
|
||||||
|
const toEnd = end.sub(this.center);
|
||||||
|
const angleStart = toStart.getAngle();
|
||||||
|
const angleEnd = toEnd.getAngle();
|
||||||
|
let angleDiff = angleEnd - angleStart;
|
||||||
|
|
||||||
|
if (angleDiff > Math.PI) {
|
||||||
|
angleDiff -= 2 * Math.PI;
|
||||||
|
} else if (angleDiff < -Math.PI) {
|
||||||
|
angleDiff += 2 * Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (angleDiff >= this.startAngle && angleDiff <= this.endAngle) {
|
||||||
|
const r = toStart.getLength();
|
||||||
|
const t = this.startAngle - angleStart;
|
||||||
|
const x = r * Math.cos(t);
|
||||||
|
const y = r * Math.sin(t);
|
||||||
|
const intersection = new Vector2(x, y);
|
||||||
|
|
||||||
|
if (intersection.isBetween(start, end)) {
|
||||||
|
const distance = intersection.sub(start).getLength();
|
||||||
|
const fraction = distance / start.getDistance(end);
|
||||||
|
const normal = intersection.sub(this.center).normalize();
|
||||||
|
const point = intersection.add(this.center);
|
||||||
|
|
||||||
|
const raycastHit = new RaycastHit();
|
||||||
|
raycastHit.setValues(fraction, distance, point, normal);
|
||||||
|
hit.value = raycastHit;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public containsPoint(point: Vector2) {
|
||||||
|
const toPoint = point.sub(this.center);
|
||||||
|
const distanceSquared = toPoint.lengthSquared();
|
||||||
|
|
||||||
|
if (distanceSquared > this.radiusSquared) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const angle = toPoint.getAngle();
|
||||||
|
const startAngle = this.startAngle;
|
||||||
|
const endAngle = startAngle + this.angle;
|
||||||
|
|
||||||
|
let angleDiff = angle - startAngle;
|
||||||
|
if (angleDiff < 0) {
|
||||||
|
angleDiff += Math.PI * 2;
|
||||||
|
}
|
||||||
|
if (angleDiff > this.angle) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public pointCollidesWithShape(point: Vector2, result: Out<CollisionResult>): boolean {
|
||||||
|
if (!this.containsPoint(point)) {
|
||||||
|
if (result) {
|
||||||
|
result.value = null;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
result.value = new CollisionResult();
|
||||||
|
result.value.normal = point.sub(this.center).normalize();
|
||||||
|
result.value.minimumTranslationVector = result.value.normal.scale(
|
||||||
|
this.radius - point.sub(this.center).getLength()
|
||||||
|
);
|
||||||
|
result.value.point = point;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPoints(): Vector2[] {
|
||||||
|
let points = new Array<Vector2>(this.numberOfPoints);
|
||||||
|
for (let i = 0; i < this.numberOfPoints; i++) {
|
||||||
|
let angle = this.startAngle + i * this.angleStep;
|
||||||
|
points[i] = Vector2.fromAngle(angle, this.radius).add(this.center);
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateProperties() {
|
||||||
|
this.numberOfPoints = Math.max(10, Math.floor(this.radius * 0.1));
|
||||||
|
this.angleStep = (this.endAngle - this.startAngle) / (this.numberOfPoints - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFurthestPoint(normal: Vector2): Vector2 {
|
||||||
|
let maxProjection = -Number.MAX_VALUE;
|
||||||
|
let furthestPoint = new Vector2();
|
||||||
|
for (let i = 0; i < this.numberOfPoints; i++) {
|
||||||
|
let projection = this.points[i].dot(normal);
|
||||||
|
if (projection > maxProjection) {
|
||||||
|
maxProjection = projection;
|
||||||
|
furthestPoint.copyFrom(this.points[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return furthestPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
module es {
|
||||||
|
export class ShapeCollisionSector {
|
||||||
|
public static sectorToPolygon(first: Sector, second: Polygon, result: Out<CollisionResult>): boolean {
|
||||||
|
const numPoints = second.points.length;
|
||||||
|
let collision = false;
|
||||||
|
const edgeStart = new Vector2();
|
||||||
|
const edgeEnd = new Vector2();
|
||||||
|
const hit = new Out<RaycastHit>();
|
||||||
|
|
||||||
|
for (let i = 0; i < numPoints; i++) {
|
||||||
|
const point = second.points[i];
|
||||||
|
if (first.containsPoint(point)) {
|
||||||
|
if (result) {
|
||||||
|
result.value = new CollisionResult();
|
||||||
|
result.value.point = point.clone();
|
||||||
|
result.value.normal = point.sub(first.center).normalize();
|
||||||
|
}
|
||||||
|
collision = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!collision && second.containsPoint(first.center)) {
|
||||||
|
if (result) {
|
||||||
|
result.value = new CollisionResult();
|
||||||
|
result.value.point = first.center.clone();
|
||||||
|
result.value.normal = new Vector2(0, 0);
|
||||||
|
}
|
||||||
|
collision = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!collision) {
|
||||||
|
for (let i = 0; i < numPoints; i++) {
|
||||||
|
const p1 = second.points[i];
|
||||||
|
const p2 = second.points[(i + 1) % numPoints];
|
||||||
|
edgeStart.copyFrom(p1);
|
||||||
|
edgeEnd.copyFrom(p2);
|
||||||
|
|
||||||
|
if (first.collidesWithLine(edgeStart, edgeEnd, hit)) {
|
||||||
|
if (result) {
|
||||||
|
result.value = new CollisionResult();
|
||||||
|
result.value.point.copyFrom(hit.value.point);
|
||||||
|
result.value.normal.copyFrom(hit.value.normal);
|
||||||
|
}
|
||||||
|
collision = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return collision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static sectorToCircle(first: Sector, second: Circle, result: Out<CollisionResult>): boolean {
|
||||||
|
const radiusSquared = second.radius * second.radius;
|
||||||
|
const distanceSquared = first.center.getDistanceSquared(second.center);
|
||||||
|
const angleDiff = Math.abs(second.center.sub(first.center).getAngle() - first.getAngle());
|
||||||
|
const sectorAngle = first.endAngle - first.startAngle;
|
||||||
|
|
||||||
|
if (distanceSquared <= radiusSquared && angleDiff <= sectorAngle / 2) {
|
||||||
|
if (result) {
|
||||||
|
result.value = new CollisionResult();
|
||||||
|
result.value.normal = second.center.sub(first.center).normalize();
|
||||||
|
result.value.point = second.center.clone().add(result.value.normal.clone().multiplyScaler(second.radius));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
result.value = null;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static sectorToBox(first: Sector, second: Box, result: Out<CollisionResult>): boolean {
|
||||||
|
result.value = new CollisionResult();
|
||||||
|
|
||||||
|
// 获取box的四条边
|
||||||
|
let boxEdges = second.getEdges();
|
||||||
|
|
||||||
|
// 遍历box的每一条边
|
||||||
|
for (let i = 0; i < boxEdges.length; i++) {
|
||||||
|
let normal = boxEdges[i].getNormal();
|
||||||
|
let furthestPointBox = second.getFurthestPoint(normal);
|
||||||
|
let furthestPointSector = first.getFurthestPoint(normal.negate());
|
||||||
|
|
||||||
|
let distance = normal.dot(furthestPointSector.sub(furthestPointBox));
|
||||||
|
|
||||||
|
// 没有相交
|
||||||
|
if (distance > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (result.value && Math.abs(distance) < result.value.minimumTranslationVector.getLength()) {
|
||||||
|
result.value.minimumTranslationVector = normal.clone().multiplyScaler(distance);
|
||||||
|
result.value.normal = normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user