diff --git a/README.md b/README.md index 5f7d816f..ef4beab6 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ class MoveSystem extends es.EntityProcessingSystem { ```typescript // 这里你可以传入多个组件类型 +// one 满足 const matcher = Matcher.empty().all(MovementComponent); ``` @@ -161,8 +162,8 @@ if( hit.Collider != null ) // 碰撞结果将包含一些非常有用的信息,例如被撞的collider,表面命中的法线和最小平移矢量(MTV)。 MTV可用于将碰撞实体直接移动到命中的碰撞器附近。 let collisionResult = null; -// 进行检查以查看entity.getComponent(实体上的第一个碰撞器)是否与场景中的任何其他碰撞器发生碰撞。请注意,如果您有多个碰撞器,则可以获取并遍历它们,而不是仅检查第一个碰撞器。 -if( entity.getComponent().collidesWithAny( deltaMovement, collisionResult ) ) +// 进行检查以查看entity.getComponent(Collider)(实体上的第一个碰撞器)是否与场景中的任何其他碰撞器发生碰撞。请注意,如果您有多个碰撞器,则可以获取并遍历它们,而不是仅检查第一个碰撞器。 +if( entity.getComponent(Collider).collidesWithAny( deltaMovement, collisionResult ) ) { // 记录CollisionResult。 您可能需要使用它来添加一些粒子效果或与您的游戏相关的任何其他内容。 console.log( `collision result: ${collisionResult}` ); @@ -178,7 +179,7 @@ entity.position = Vector2.add(entity.position, deltaMovement); let collisionResult = null; // 进行检查以查看entity.getComponent是否与一些其他Collider发生碰撞 -if( entity.getComponent().collidesWith( someOtherCollider, deltaMovement, collisionResult ) ) +if( entity.getComponent(Collider).collidesWith( someOtherCollider, deltaMovement, collisionResult ) ) { // 将实体移动到与命中Collider相邻的位置,然后记录CollisionResult entity.position = Vector2.add(entity.position, Vector2.substract(deltaMovement, collisionResult.minimumTranslationVector)); @@ -189,12 +190,12 @@ if( entity.getComponent().collidesWith( someOtherCollider, deltaMoveme ```ts // 在我们自身以外的位置获取可能与之重叠的任何东西 -let neighborColliders = Physics.boxcastBroadphaseExcludingSelf( entity.getComponent() ); +let neighborColliders = Physics.boxcastBroadphaseExcludingSelf( entity.getComponent(Collider) ); // 遍历并检查每个对撞机是否重叠 for( let collider of neighborColliders ) { - if( entity.getComponent().overlaps( collider ) ) + if( entity.getComponent(Collider).overlaps( collider ) ) console.log( `我们正在重叠一个collider : ${collider}` ); } ``` diff --git a/source/bin/framework.d.ts b/source/bin/framework.d.ts index dc9b85b9..34c11139 100644 --- a/source/bin/framework.d.ts +++ b/source/bin/framework.d.ts @@ -457,6 +457,13 @@ declare module es { * @param value2 */ static multiply(value1: Vector2, value2: Vector2): Vector2; + /** + * + * @param value1 + * @param value2 + * @returns + */ + static multiplyScaler(value1: Vector2, value2: number): Vector2; /** * * @param value1 @@ -496,12 +503,26 @@ declare module es { * @returns 指定向量的线性插值结果 */ static lerp(value1: Vector2, value2: Vector2, amount: number): Vector2; + /** + * 创建一个新的Vector2,其中包含指定矢量的线性插值 + * @param value1 + * @param value2 + * @param amount + * @returns + */ + static lerpPrecise(value1: Vector2, value2: Vector2, amount: number): Vector2; /** * 创建一个新的Vector2,该Vector2包含了通过指定的Matrix进行的二维向量变换。 * @param position * @param matrix */ static transform(position: Vector2, matrix: Matrix2D): Vector2; + /** + * 创建一个新的Vector2,其中包含由指定的Matrix转换的指定法线 + * @param normal + * @param matrix + */ + static transformNormal(normal: Vector2, matrix: Matrix): Vector2; /** * 返回两个向量之间的距离 * @param value1 @@ -521,6 +542,21 @@ declare module es { * @returns 矢量反演的结果 */ static negate(value: Vector2): Vector2; + /** + * 创建一个新的Vector2,其中包含给定矢量和法线的反射矢量 + * @param vector + * @param normal + * @returns + */ + static reflect(vector: Vector2, normal: Vector2): Vector2; + /** + * 创建一个新的Vector2,其中包含指定矢量的三次插值 + * @param value1 + * @param value2 + * @param amount + * @returns + */ + static smoothStep(value1: Vector2, value2: Vector2, amount: number): Vector2; /** * * @param value @@ -536,6 +572,12 @@ declare module es { * @param value */ multiply(value: Vector2): Vector2; + /** + * + * @param value + * @returns + */ + multiplyScaler(value: number): Vector2; /** * 从当前Vector2减去一个Vector2 * @param value 要减去的Vector2 @@ -569,6 +611,31 @@ declare module es { * @returns 如果实例相同true 否则false */ equals(other: Vector2 | object): boolean; + isValid(): boolean; + /** + * 创建一个新的Vector2,其中包含来自两个向量的最小值 + * @param value1 + * @param value2 + * @returns + */ + static min(value1: Vector2, value2: Vector2): Vector2; + /** + * 创建一个新的Vector2,其中包含两个向量的最大值 + * @param value1 + * @param value2 + * @returns + */ + static max(value1: Vector2, value2: Vector2): Vector2; + /** + * 创建一个新的Vector2,其中包含Hermite样条插值 + * @param value1 + * @param tangent1 + * @param value2 + * @param tangent2 + * @param amount + * @returns + */ + static hermite(value1: Vector2, tangent1: Vector2, value2: Vector2, tangent2: Vector2, amount: number): Vector2; clone(): Vector2; } } @@ -2189,7 +2256,25 @@ declare module es { */ static toRadians(degrees: number): number; /** - * mapps值(在leftMin - leftMax范围内)到rightMin - rightMax范围内的值 + * 返回由给定三角形和两个归一化重心(面积)坐标定义的点的一个轴的笛卡尔坐标 + * @param value1 + * @param value2 + * @param value3 + * @param amount1 + * @param amount2 + */ + static barycentric(value1: number, value2: number, value3: number, amount1: number, amount2: number): number; + /** + * 使用指定位置执行Catmull-Rom插值 + * @param value1 + * @param value2 + * @param value3 + * @param value4 + * @param amount + */ + static catmullRom(value1: number, value2: number, value3: number, value4: number, amount: number): number; + /** + * 将值(在leftMin-leftMax范围内)映射到一个在rightMin-rightMax范围内的值 * @param value * @param leftMin * @param leftMax @@ -2197,6 +2282,41 @@ declare module es { * @param rightMax */ static map(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax: number): number; + /** + * 将值从任意范围映射到0到1范围 + * @param value + * @param min + * @param max + * @returns + */ + static map01(value: number, min: number, max: number): number; + /** + * 将值从某个任意范围映射到1到0范围 + * 这相当于map01的取反 + * @param value + * @param min + * @param max + * @returns + */ + static map10(value: number, min: number, max: number): number; + /** + * 使用三次方程在两个值之间进行插值 + * @param value1 + * @param value2 + * @param amount + */ + static smoothStep(value1: number, value2: number, amount: number): number; + /** + * 将给定角度减小到π到-π之间的值 + * @param angle + */ + static wrapAngle(angle: number): number; + /** + * 确定值是否以2为底 + * @param value + * @returns + */ + static isPowerOfTwo(value: number): boolean; static lerp(from: number, to: number, t: number): number; /** * 使度数的角度在a和b之间 @@ -2230,6 +2350,11 @@ declare module es { */ static signThreshold(value: number, threshold: number): number; static inverseLerp(from: number, to: number, t: number): number; + /** + * 在两个值之间线性插值 + * 此方法是MathHelper.Lerp的效率较低,更精确的版本。 + */ + static lerpPrecise(value1: number, value2: number, amount: number): number; static clamp(value: number, min: number, max: number): number; static snap(value: number, increment: number): number; /** @@ -2244,6 +2369,19 @@ declare module es { * @param value */ static isEven(value: number): boolean; + /** + * 如果值是奇数,则返回true + * @param value + * @returns + */ + static isOdd(value: number): boolean; + /** + * 将值四舍五入并返回它和四舍五入后的数值 + * @param value + * @param roundedAmount + * @returns + */ + static roundWithRoundedAmount(value: number, roundedAmount: Ref): number; /** * 数值限定在0-1之间 * @param value @@ -2257,6 +2395,21 @@ declare module es { * @param length */ static incrementWithWrap(t: number, length: number): number; + /** + * 递减t并确保其始终大于或等于0且小于长度 + * @param t + * @param length + * @returns + */ + static decrementWithWrap(t: number, length: number): number; + /** + * 返回sqrt(x * x + y * y) + * @param x + * @param y + * @returns + */ + static hypotenuse(x: number, y: number): number; + static closestPowerOfTwoGreaterThan(x: number): number; /** * 以roundToNearest为步长,将值舍入到最接近的数字。例如:在125中找到127到最近的5个结果 * @param value @@ -2310,6 +2463,14 @@ declare module es { * @param target */ static deltaAngle(current: number, target: number): number; + /** + * 检查值是否介于最小值/最大值(包括最小值/最大值)之间 + * @param value + * @param min + * @param max + * @returns + */ + static between(value: number, min: number, max: number): boolean; /** * 计算以弧度为单位的两个给定角度之间的最短差 * @param current @@ -2377,6 +2538,22 @@ declare module es { * @returns */ static lissajouDamped(xFrequency?: number, yFrequency?: number, xMagnitude?: number, yMagnitude?: number, phase?: number, damping?: number, oscillationInterval?: number): Vector2; + /** + * 执行Hermite样条插值 + * @param value1 + * @param tangent1 + * @param value2 + * @param tangent2 + * @param amount + * @returns + */ + static hermite(value1: number, tangent1: number, value2: number, tangent2: number, amount: number): any; + /** + * 此函数用于确保数不是NaN或无穷大 + * @param x + * @returns + */ + static isValid(x: number): boolean; } } declare module es { @@ -4374,6 +4551,38 @@ declare module es { * 如果矩形不相交,则返回Vector2.zero。 */ static getIntersectionDepth(rectA: Rectangle, rectB: Rectangle): Vector2; + static getClosestPointOnBoundsToOrigin(rect: Rectangle): Vector2; + /** + * 将Rectangle中或上的最接近点返回给定点 + * @param rect + * @param point + */ + static getClosestPointOnRectangleToPoint(rect: Rectangle, point: Vector2): Vector2; + /** + * 获取矩形边界上与给定点最接近的点 + * @param rect + * @param point + */ + static getClosestPointOnRectangleBorderToPoint(rect: Rectangle, point: Vector2): Vector2; + static getMax(rect: Rectangle): Vector2; + /** + * 以Vector2的形式获取矩形的中心点 + * @param rect + * @returns + */ + static getCenter(rect: Rectangle): Vector2; + /** + * 给定多边形的点即可计算边界 + * @param points + */ + static boundsFromPolygonPoints(points: Vector2[]): Rectangle; + /** + * 缩放矩形 + * @param rect + * @param scale + */ + static scale(rect: Rectangle, scale: Vector2): void; + static translate(rect: Rectangle, vec: Vector2): void; } } declare module es { @@ -4419,6 +4628,13 @@ declare module es { * @param to */ static angle(from: Vector2, to: Vector2): number; + /** + * 返回以自度为中心的左右角度 + * @param self + * @param left + * @param right + */ + static angleBetween(self: Vector2, left: Vector2, right: Vector2): number; /** * 给定两条直线(ab和cd),求交点 * @param a diff --git a/source/bin/framework.js b/source/bin/framework.js index fabd5e8b..5a673350 100644 --- a/source/bin/framework.js +++ b/source/bin/framework.js @@ -1138,6 +1138,18 @@ var es; result.y = value1.y * value2.y; return result; }; + /** + * + * @param value1 + * @param value2 + * @returns + */ + Vector2.multiplyScaler = function (value1, value2) { + var result = new Vector2(0, 0); + result.x = value1.x * value2; + result.y = value1.x * value2; + return result; + }; /** * * @param value1 @@ -1197,6 +1209,16 @@ var es; Vector2.lerp = function (value1, value2, amount) { return new Vector2(es.MathHelper.lerp(value1.x, value2.x, amount), es.MathHelper.lerp(value1.y, value2.y, amount)); }; + /** + * 创建一个新的Vector2,其中包含指定矢量的线性插值 + * @param value1 + * @param value2 + * @param amount + * @returns + */ + Vector2.lerpPrecise = function (value1, value2, amount) { + return new Vector2(es.MathHelper.lerpPrecise(value1.x, value2.x, amount), es.MathHelper.lerpPrecise(value1.y, value2.y, amount)); + }; /** * 创建一个新的Vector2,该Vector2包含了通过指定的Matrix进行的二维向量变换。 * @param position @@ -1205,6 +1227,14 @@ var es; Vector2.transform = function (position, matrix) { return new Vector2((position.x * matrix.m11) + (position.y * matrix.m21) + matrix.m31, (position.x * matrix.m12) + (position.y * matrix.m22) + matrix.m32); }; + /** + * 创建一个新的Vector2,其中包含由指定的Matrix转换的指定法线 + * @param normal + * @param matrix + */ + Vector2.transformNormal = function (normal, matrix) { + return new Vector2((normal.x * matrix.m11) + (normal.y * matrix.m21), (normal.x * matrix.m12) + (normal.y * matrix.m22)); + }; /** * 返回两个向量之间的距离 * @param value1 @@ -1235,6 +1265,29 @@ var es; value.y = -value.y; return value; }; + /** + * 创建一个新的Vector2,其中包含给定矢量和法线的反射矢量 + * @param vector + * @param normal + * @returns + */ + Vector2.reflect = function (vector, normal) { + var result = new Vector2(); + var val = 2 * ((vector.x * normal.x) + (vector.y * normal.y)); + result.x = vector.x - (normal.x * val); + result.y = vector.y - (normal.y * val); + return result; + }; + /** + * 创建一个新的Vector2,其中包含指定矢量的三次插值 + * @param value1 + * @param value2 + * @param amount + * @returns + */ + Vector2.smoothStep = function (value1, value2, amount) { + return new Vector2(es.MathHelper.smoothStep(value1.x, value2.x, amount), es.MathHelper.smoothStep(value1.y, value2.y, amount)); + }; /** * * @param value @@ -1262,6 +1315,16 @@ var es; this.y *= value.y; return this; }; + /** + * + * @param value + * @returns + */ + Vector2.prototype.multiplyScaler = function (value) { + this.x *= value; + this.y *= value; + return this; + }; /** * 从当前Vector2减去一个Vector2 * @param value 要减去的Vector2 @@ -1318,6 +1381,39 @@ var es; } return false; }; + Vector2.prototype.isValid = function () { + return es.MathHelper.isValid(this.x) && es.MathHelper.isValid(this.y); + }; + /** + * 创建一个新的Vector2,其中包含来自两个向量的最小值 + * @param value1 + * @param value2 + * @returns + */ + Vector2.min = function (value1, value2) { + return new Vector2(value1.x < value2.x ? value1.x : value2.x, value1.y < value2.y ? value1.y : value2.y); + }; + /** + * 创建一个新的Vector2,其中包含两个向量的最大值 + * @param value1 + * @param value2 + * @returns + */ + Vector2.max = function (value1, value2) { + return new Vector2(value1.x > value2.x ? value1.x : value2.x, value1.y > value2.y ? value1.y : value2.y); + }; + /** + * 创建一个新的Vector2,其中包含Hermite样条插值 + * @param value1 + * @param tangent1 + * @param value2 + * @param tangent2 + * @param amount + * @returns + */ + Vector2.hermite = function (value1, tangent1, value2, tangent2, amount) { + return new Vector2(es.MathHelper.hermite(value1.x, tangent1.x, value2.x, tangent2.x, amount), es.MathHelper.hermite(value1.y, tangent1.y, value2.y, tangent2.y, amount)); + }; Vector2.prototype.clone = function () { return new Vector2(this.x, this.y); }; @@ -2232,8 +2328,8 @@ var es; */ ArcadeRigidbody.prototype.addImpulse = function (force) { if (!this.isImmovable) { - this.velocity = es.Vector2.add(this.velocity, es.Vector2.multiply(force, new es.Vector2(100000)) - .multiply(new es.Vector2(this._inverseMass * es.Time.deltaTime))); + this.velocity = this.velocity.add(es.Vector2.multiplyScaler(force, 100000) + .multiplyScaler(this._inverseMass * es.Time.deltaTime * es.Time.deltaTime)); } }; ArcadeRigidbody.prototype.onAddedToEntity = function () { @@ -2247,8 +2343,8 @@ var es; return; } if (this.shouldUseGravity) - this.velocity = es.Vector2.add(this.velocity, es.Vector2.multiply(es.Physics.gravity, new es.Vector2(es.Time.deltaTime))); - this.entity.transform.position = es.Vector2.add(this.entity.transform.position, es.Vector2.multiply(this.velocity, new es.Vector2(es.Time.deltaTime))); + this.velocity = this.velocity.add(es.Vector2.multiplyScaler(es.Physics.gravity, es.Time.deltaTime)); + this.entity.transform.position = this.entity.transform.position.add(es.Vector2.multiplyScaler(this.velocity, es.Time.deltaTime)); var collisionResult = new es.CollisionResult(); // 捞取我们在新的位置上可能会碰撞到的任何东西 var neighbors = es.Physics.boxcastBroadphaseExcludingSelfNonRect(this._collider, this._collider.collidesWithLayers.value); @@ -2268,10 +2364,10 @@ var es; } else { // 没有ArcadeRigidbody,所以我们假设它是不动的,只移动我们自己的 - this.entity.transform.position = es.Vector2.subtract(this.entity.transform.position, collisionResult.minimumTranslationVector); + this.entity.transform.position = this.entity.transform.position.subtract(collisionResult.minimumTranslationVector); var relativeVelocity = this.velocity.clone(); this.calculateResponseVelocity(relativeVelocity, collisionResult.minimumTranslationVector, relativeVelocity); - this.velocity = es.Vector2.add(this.velocity, relativeVelocity); + this.velocity = this.velocity.add(relativeVelocity); } } } @@ -2291,14 +2387,14 @@ var es; */ ArcadeRigidbody.prototype.processOverlap = function (other, minimumTranslationVector) { if (this.isImmovable) { - other.entity.transform.position = es.Vector2.add(other.entity.transform.position, minimumTranslationVector); + other.entity.transform.position = other.entity.transform.position.add(minimumTranslationVector); } else if (other.isImmovable) { - this.entity.transform.position = es.Vector2.subtract(this.entity.transform.position, minimumTranslationVector); + this.entity.transform.position = this.entity.transform.position.subtract(minimumTranslationVector); } else { - this.entity.transform.position = es.Vector2.subtract(this.entity.transform.position, es.Vector2.multiply(minimumTranslationVector, es.Vector2Ext.halfVector())); - other.entity.transform.position = es.Vector2.add(other.entity.transform.position, es.Vector2.multiply(minimumTranslationVector, es.Vector2Ext.halfVector())); + this.entity.transform.position = this.entity.transform.position.subtract(es.Vector2.multiplyScaler(minimumTranslationVector, 0.5)); + other.entity.transform.position = other.entity.transform.position.add(es.Vector2.multiplyScaler(minimumTranslationVector, 0.5)); } }; /** @@ -2316,8 +2412,8 @@ var es; var totalinverseMass = this._inverseMass + other._inverseMass; var ourResponseFraction = this._inverseMass / totalinverseMass; var otherResponseFraction = other._inverseMass / totalinverseMass; - this.velocity = es.Vector2.add(this.velocity, new es.Vector2(relativeVelocity.x * ourResponseFraction, relativeVelocity.y * ourResponseFraction)); - other.velocity = es.Vector2.subtract(other.velocity, new es.Vector2(relativeVelocity.x * otherResponseFraction, relativeVelocity.y * otherResponseFraction)); + this.velocity = this.velocity.add(es.Vector2.multiplyScaler(relativeVelocity, ourResponseFraction)); + other.velocity = other.velocity.subtract(es.Vector2.multiplyScaler(relativeVelocity, otherResponseFraction)); }; /** * 给定两个物体和MTV之间的相对速度,本方法修改相对速度,使其成为碰撞响应 @@ -2328,12 +2424,12 @@ var es; ArcadeRigidbody.prototype.calculateResponseVelocity = function (relativeVelocity, minimumTranslationVector, responseVelocity) { if (responseVelocity === void 0) { responseVelocity = new es.Vector2(); } // 首先,我们得到反方向的归一化MTV:表面法线 - var inverseMTV = es.Vector2.multiply(minimumTranslationVector, new es.Vector2(-1)); + var inverseMTV = es.Vector2.multiplyScaler(minimumTranslationVector, -1); var normal = es.Vector2.normalize(inverseMTV); // 速度是沿碰撞法线和碰撞平面分解的。 // 弹性将影响沿法线的响应(法线速度分量),摩擦力将影响速度的切向分量(切向速度分量) var n = es.Vector2.dot(relativeVelocity, normal); - var normalVelocityComponent = new es.Vector2(normal.x * n, normal.y * n); + var normalVelocityComponent = es.Vector2.multiplyScaler(normal, n); var tangentialVelocityComponent = es.Vector2.subtract(relativeVelocity, normalVelocityComponent); if (n > 0) normalVelocityComponent = es.Vector2.zero; @@ -2342,11 +2438,8 @@ var es; if (tangentialVelocityComponent.lengthSquared() < this._glue) coefficientOfFriction = 1.01; // 弹性影响速度的法向分量,摩擦力影响速度的切向分量 - var t = es.Vector2.multiply(new es.Vector2((1 + this._elasticity)), normalVelocityComponent) - .multiply(new es.Vector2(-1)) - .subtract(es.Vector2.multiply(new es.Vector2(coefficientOfFriction), tangentialVelocityComponent)); - responseVelocity.x = t.x; - relativeVelocity.y = t.y; + responseVelocity = es.Vector2.multiplyScaler(normalVelocityComponent, -(1 + this._elasticity)) + .subtract(es.Vector2.multiplyScaler(tangentialVelocityComponent, coefficientOfFriction)); }; return ArcadeRigidbody; }(es.Component)); @@ -4732,7 +4825,7 @@ var es; currentTime = Date.now(); if (this._lastTime == -1) this._lastTime = currentTime; - var dt = currentTime - this._lastTime; + var dt = (currentTime - this._lastTime) / 1000; if (dt > this.maxDeltaTime) dt = this.maxDeltaTime; this.totalTime += dt; @@ -5332,7 +5425,34 @@ var es; return degrees * 0.017453292519943295769236907684886; }; /** - * mapps值(在leftMin - leftMax范围内)到rightMin - rightMax范围内的值 + * 返回由给定三角形和两个归一化重心(面积)坐标定义的点的一个轴的笛卡尔坐标 + * @param value1 + * @param value2 + * @param value3 + * @param amount1 + * @param amount2 + */ + MathHelper.barycentric = function (value1, value2, value3, amount1, amount2) { + return value1 + (value2 - value1) * amount1 + (value3 - value1) * amount2; + }; + /** + * 使用指定位置执行Catmull-Rom插值 + * @param value1 + * @param value2 + * @param value3 + * @param value4 + * @param amount + */ + MathHelper.catmullRom = function (value1, value2, value3, value4, amount) { + // 使用来自http://www.mvps.org/directx/articles/catmull/的公式 + var amountSquared = amount * amount; + var amountCubed = amountSquared * amount; + return (0.5 * (2 * value2 + (value3 - value1) * amount + + (2 * value1 - 5 * value2 + 4 * value3 - value4) * amountSquared + + (3 * value2 - value1 - 3 * value3 + value4) * amountCubed)); + }; + /** + * 将值(在leftMin-leftMax范围内)映射到一个在rightMin-rightMax范围内的值 * @param value * @param leftMin * @param leftMax @@ -5342,6 +5462,60 @@ var es; MathHelper.map = function (value, leftMin, leftMax, rightMin, rightMax) { return rightMin + (value - leftMin) * (rightMax - rightMin) / (leftMax - leftMin); }; + /** + * 将值从任意范围映射到0到1范围 + * @param value + * @param min + * @param max + * @returns + */ + MathHelper.map01 = function (value, min, max) { + return (value - min) * 1 / (max - min); + }; + /** + * 将值从某个任意范围映射到1到0范围 + * 这相当于map01的取反 + * @param value + * @param min + * @param max + * @returns + */ + MathHelper.map10 = function (value, min, max) { + return 1 - this.map01(value, min, max); + }; + /** + * 使用三次方程在两个值之间进行插值 + * @param value1 + * @param value2 + * @param amount + */ + MathHelper.smoothStep = function (value1, value2, amount) { + var result = this.clamp(amount, 0, 1); + result = MathHelper.hermite(value1, 0, value2, 0, result); + return result; + }; + /** + * 将给定角度减小到π到-π之间的值 + * @param angle + */ + MathHelper.wrapAngle = function (angle) { + if ((angle > -Math.PI) && (angle <= Math.PI)) + return angle; + angle %= Math.PI * 2; + if (angle <= -Math.PI) + return angle + 2 * Math.PI; + if (angle > Math.PI) + return angle - 2 * Math.PI; + return angle; + }; + /** + * 确定值是否以2为底 + * @param value + * @returns + */ + MathHelper.isPowerOfTwo = function (value) { + return (value > 0) && ((value % (value - 1)) == 0); + }; MathHelper.lerp = function (from, to, t) { return from + (to - from) * this.clamp01(t); }; @@ -5409,6 +5583,13 @@ var es; } return (t - from) / (to - from); }; + /** + * 在两个值之间线性插值 + * 此方法是MathHelper.Lerp的效率较低,更精确的版本。 + */ + MathHelper.lerpPrecise = function (value1, value2, amount) { + return ((1 - amount) * value1) + (value2 * amount); + }; MathHelper.clamp = function (value, min, max) { if (value < min) return min; @@ -5436,6 +5617,25 @@ var es; MathHelper.isEven = function (value) { return value % 2 == 0; }; + /** + * 如果值是奇数,则返回true + * @param value + * @returns + */ + MathHelper.isOdd = function (value) { + return value % 2 != 0; + }; + /** + * 将值四舍五入并返回它和四舍五入后的数值 + * @param value + * @param roundedAmount + * @returns + */ + MathHelper.roundWithRoundedAmount = function (value, roundedAmount) { + var rounded = Math.round(value); + roundedAmount.value = value - (rounded * Math.round(value / rounded)); + return rounded; + }; /** * 数值限定在0-1之间 * @param value @@ -5464,6 +5664,36 @@ var es; return 0; return t; }; + /** + * 递减t并确保其始终大于或等于0且小于长度 + * @param t + * @param length + * @returns + */ + MathHelper.decrementWithWrap = function (t, length) { + t--; + if (t < 0) + return length - 1; + return t; + }; + /** + * 返回sqrt(x * x + y * y) + * @param x + * @param y + * @returns + */ + MathHelper.hypotenuse = function (x, y) { + return Math.sqrt(x * x + y * y); + }; + MathHelper.closestPowerOfTwoGreaterThan = function (x) { + x--; + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return (x + 1); + }; /** * 以roundToNearest为步长,将值舍入到最接近的数字。例如:在125中找到127到最近的5个结果 * @param value @@ -5544,6 +5774,16 @@ var es; num -= 360; return num; }; + /** + * 检查值是否介于最小值/最大值(包括最小值/最大值)之间 + * @param value + * @param min + * @param max + * @returns + */ + MathHelper.between = function (value, min, max) { + return value >= min && value <= max; + }; /** * 计算以弧度为单位的两个给定角度之间的最短差 * @param current @@ -5657,6 +5897,41 @@ var es; var y = damped * Math.cos(es.Time.totalTime * yFrequency) * yMagnitude; return new es.Vector2(x, y); }; + /** + * 执行Hermite样条插值 + * @param value1 + * @param tangent1 + * @param value2 + * @param tangent2 + * @param amount + * @returns + */ + MathHelper.hermite = function (value1, tangent1, value2, tangent2, amount) { + var v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount, result; + var sCubed = s * s * s; + var sSquared = s * s; + if (amount == 0) + result = value1; + else if (amount == 1) + result = value2; + else + result = (2 * v1 - 2 * v2 + t2 + t1) * sCubed + + (3 * v2 - 3 * v1 - 2 * t1 - t2) * sSquared + + t1 * s + + v1; + return result; + }; + /** + * 此函数用于确保数不是NaN或无穷大 + * @param x + * @returns + */ + MathHelper.isValid = function (x) { + if (Number.isNaN(x)) { + return false; + } + return !Number.isFinite(x); + }; MathHelper.Epsilon = 0.00001; MathHelper.Rad2Deg = 57.29578; MathHelper.Deg2Rad = 0.0174532924; @@ -11422,6 +11697,115 @@ var es; var depthY = distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY; return new es.Vector2(depthX, depthY); }; + RectangleExt.getClosestPointOnBoundsToOrigin = function (rect) { + var max = this.getMax(rect); + var minDist = Math.abs(rect.location.x); + var boundsPoint = new es.Vector2(rect.location.x, 0); + if (Math.abs(max.x) < minDist) { + minDist = Math.abs(max.x); + boundsPoint.x = max.x; + boundsPoint.y = 0; + } + if (Math.abs(max.y) < minDist) { + minDist = Math.abs(max.y); + boundsPoint.x = 0; + boundsPoint.y = max.y; + } + if (Math.abs(rect.location.y) < minDist) { + minDist = Math.abs(rect.location.y); + boundsPoint.x = 0; + boundsPoint.y = rect.location.y; + } + return boundsPoint; + }; + /** + * 将Rectangle中或上的最接近点返回给定点 + * @param rect + * @param point + */ + RectangleExt.getClosestPointOnRectangleToPoint = function (rect, point) { + // 对于每个轴,如果该点在盒子外面,则将在盒子上,否则不理会它 + var res = new es.Vector2(); + res.x = es.MathHelper.clamp(point.x, rect.left, rect.right); + res.y = es.MathHelper.clamp(point.y, rect.top, rect.bottom); + return res; + }; + /** + * 获取矩形边界上与给定点最接近的点 + * @param rect + * @param point + */ + RectangleExt.getClosestPointOnRectangleBorderToPoint = function (rect, point) { + // 对于每个轴,如果该点在盒子外面,则将在盒子上,否则不理会它 + var res = new es.Vector2(); + res.x = es.MathHelper.clamp(point.x, rect.left, rect.right); + res.y = es.MathHelper.clamp(point.y, rect.top, rect.bottom); + // 如果点在矩形内,我们需要将res推到边框,因为它将在矩形内 + if (rect.contains(res.x, res.y)) { + var dl = rect.x - rect.left; + var dr = rect.right - res.x; + var dt = res.y - rect.top; + var db = rect.bottom - res.y; + var min = Math.min(dl, dr, dt, db); + if (min == dt) + res.y = rect.top; + else if (min == db) + res.y = rect.bottom; + else if (min == dl) + res.x == rect.left; + else + res.x = rect.right; + } + return res; + }; + RectangleExt.getMax = function (rect) { + return new es.Vector2(rect.right, rect.bottom); + }; + /** + * 以Vector2的形式获取矩形的中心点 + * @param rect + * @returns + */ + RectangleExt.getCenter = function (rect) { + return new es.Vector2(rect.x + rect.width / 2, rect.y + rect.height / 2); + }; + /** + * 给定多边形的点即可计算边界 + * @param points + */ + RectangleExt.boundsFromPolygonPoints = function (points) { + // 我们需要找到最小/最大x / y值 + var minX = Number.POSITIVE_INFINITY; + var minY = Number.POSITIVE_INFINITY; + var maxX = Number.NEGATIVE_INFINITY; + var maxY = Number.NEGATIVE_INFINITY; + for (var i = 0; i < points.length; i++) { + var pt = points[i]; + if (pt.x < minX) + minX = pt.x; + if (pt.x > maxX) + maxX = pt.x; + if (pt.y < minY) + minY = pt.y; + if (pt.y > maxY) + maxY = pt.y; + } + return this.fromMinMaxVector(new es.Vector2(minX, minY), new es.Vector2(maxX, maxY)); + }; + /** + * 缩放矩形 + * @param rect + * @param scale + */ + RectangleExt.scale = function (rect, scale) { + rect.x = rect.x * scale.x; + rect.y = rect.y * scale.y; + rect.width = rect.width * scale.x; + rect.height = rect.height * scale.y; + }; + RectangleExt.translate = function (rect, vec) { + rect.location.add(vec); + }; return RectangleExt; }()); es.RectangleExt = RectangleExt; @@ -11508,6 +11892,17 @@ var es; this.normalize(to); return Math.acos(es.MathHelper.clamp(es.Vector2.dot(from, to), -1, 1)) * es.MathHelper.Rad2Deg; }; + /** + * 返回以自度为中心的左右角度 + * @param self + * @param left + * @param right + */ + Vector2Ext.angleBetween = function (self, left, right) { + var one = es.Vector2.subtract(left, self); + var two = es.Vector2.subtract(right, self); + return this.angle(one, two); + }; /** * 给定两条直线(ab和cd),求交点 * @param a diff --git a/source/src/ECS/Components/Physics/ArcadeRigidbody.ts b/source/src/ECS/Components/Physics/ArcadeRigidbody.ts index 73ec40d3..596329ba 100644 --- a/source/src/ECS/Components/Physics/ArcadeRigidbody.ts +++ b/source/src/ECS/Components/Physics/ArcadeRigidbody.ts @@ -122,8 +122,8 @@ module es { */ public addImpulse(force: Vector2) { if (!this.isImmovable) { - this.velocity = Vector2.add(this.velocity, Vector2.multiply(force, new Vector2(100000)) - .multiply(new Vector2(this._inverseMass * Time.deltaTime))); + this.velocity = this.velocity.add(Vector2.multiplyScaler(force, 100000) + .multiplyScaler(this._inverseMass * Time.deltaTime * Time.deltaTime)); } } @@ -139,9 +139,9 @@ module es { } if (this.shouldUseGravity) - this.velocity = Vector2.add(this.velocity, Vector2.multiply(Physics.gravity, new Vector2(Time.deltaTime))); + this.velocity = this.velocity.add(Vector2.multiplyScaler(Physics.gravity, Time.deltaTime)); - this.entity.transform.position = Vector2.add(this.entity.transform.position, Vector2.multiply(this.velocity, new Vector2(Time.deltaTime))); + this.entity.transform.position = this.entity.transform.position.add(Vector2.multiplyScaler(this.velocity, Time.deltaTime)); let collisionResult = new CollisionResult(); // 捞取我们在新的位置上可能会碰撞到的任何东西 @@ -154,16 +154,16 @@ module es { if (this._collider.collidesWithNonMotion(neighbor, collisionResult)) { // 如果附近有一个ArcadeRigidbody,我们就会处理完整的碰撞响应。如果没有,我们会根据附近是不可移动的来计算事情 - let neighborRigidbody = neighbor.entity.getComponent(ArcadeRigidbody); + let neighborRigidbody = neighbor.entity.getComponent(ArcadeRigidbody); if (neighborRigidbody != null) { this.processOverlap(neighborRigidbody, collisionResult.minimumTranslationVector); this.processCollision(neighborRigidbody, collisionResult.minimumTranslationVector); } else { // 没有ArcadeRigidbody,所以我们假设它是不动的,只移动我们自己的 - this.entity.transform.position = Vector2.subtract(this.entity.transform.position, collisionResult.minimumTranslationVector); + this.entity.transform.position = this.entity.transform.position.subtract(collisionResult.minimumTranslationVector); let relativeVelocity = this.velocity.clone(); this.calculateResponseVelocity(relativeVelocity, collisionResult.minimumTranslationVector, relativeVelocity); - this.velocity = Vector2.add(this.velocity, relativeVelocity); + this.velocity = this.velocity.add(relativeVelocity); } } } @@ -176,12 +176,12 @@ module es { */ public processOverlap(other: ArcadeRigidbody, minimumTranslationVector: Vector2) { if (this.isImmovable) { - other.entity.transform.position = Vector2.add(other.entity.transform.position, minimumTranslationVector); + other.entity.transform.position = other.entity.transform.position.add(minimumTranslationVector); } else if (other.isImmovable) { - this.entity.transform.position = Vector2.subtract(this.entity.transform.position, minimumTranslationVector); + this.entity.transform.position = this.entity.transform.position.subtract(minimumTranslationVector); } else { - this.entity.transform.position = Vector2.subtract(this.entity.transform.position, Vector2.multiply(minimumTranslationVector, Vector2Ext.halfVector())); - other.entity.transform.position = Vector2.add(other.entity.transform.position, Vector2.multiply(minimumTranslationVector, Vector2Ext.halfVector())); + this.entity.transform.position = this.entity.transform.position.subtract(Vector2.multiplyScaler(minimumTranslationVector, 0.5)); + other.entity.transform.position = other.entity.transform.position.add(Vector2.multiplyScaler(minimumTranslationVector, 0.5)); } } @@ -203,8 +203,8 @@ module es { let ourResponseFraction = this._inverseMass / totalinverseMass; let otherResponseFraction = other._inverseMass / totalinverseMass; - this.velocity = Vector2.add(this.velocity, new Vector2(relativeVelocity.x * ourResponseFraction, relativeVelocity.y * ourResponseFraction)); - other.velocity = Vector2.subtract(other.velocity, new Vector2(relativeVelocity.x * otherResponseFraction, relativeVelocity.y * otherResponseFraction)); + this.velocity = this.velocity.add(Vector2.multiplyScaler(relativeVelocity, ourResponseFraction)); + other.velocity = other.velocity.subtract(Vector2.multiplyScaler(relativeVelocity, otherResponseFraction)); } /** @@ -215,14 +215,14 @@ module es { */ public calculateResponseVelocity(relativeVelocity: Vector2, minimumTranslationVector: Vector2, responseVelocity: Vector2 = new Vector2()) { // 首先,我们得到反方向的归一化MTV:表面法线 - let inverseMTV = Vector2.multiply(minimumTranslationVector, new Vector2(-1)); + let inverseMTV = Vector2.multiplyScaler(minimumTranslationVector, -1); let normal = Vector2.normalize(inverseMTV); // 速度是沿碰撞法线和碰撞平面分解的。 // 弹性将影响沿法线的响应(法线速度分量),摩擦力将影响速度的切向分量(切向速度分量) let n = Vector2.dot(relativeVelocity, normal); - let normalVelocityComponent = new Vector2(normal.x * n, normal.y * n); + let normalVelocityComponent = Vector2.multiplyScaler(normal, n); let tangentialVelocityComponent = Vector2.subtract(relativeVelocity, normalVelocityComponent); if (n > 0) @@ -234,11 +234,8 @@ module es { coefficientOfFriction = 1.01; // 弹性影响速度的法向分量,摩擦力影响速度的切向分量 - let t = Vector2.multiply(new Vector2((1 + this._elasticity)), normalVelocityComponent) - .multiply(new Vector2(-1)) - .subtract(Vector2.multiply(new Vector2(coefficientOfFriction), tangentialVelocityComponent)); - responseVelocity.x = t.x; - relativeVelocity.y = t.y; + responseVelocity = Vector2.multiplyScaler(normalVelocityComponent, -(1 + this._elasticity)) + .subtract(Vector2.multiplyScaler(tangentialVelocityComponent, coefficientOfFriction)); } } } \ No newline at end of file diff --git a/source/src/ECS/Utils/Time.ts b/source/src/ECS/Utils/Time.ts index 7798b4eb..0d227569 100644 --- a/source/src/ECS/Utils/Time.ts +++ b/source/src/ECS/Utils/Time.ts @@ -23,7 +23,7 @@ module es { if (this._lastTime == -1) this._lastTime = currentTime; - let dt = currentTime - this._lastTime; + let dt = (currentTime - this._lastTime) / 1000; if (dt > this.maxDeltaTime) dt = this.maxDeltaTime; this.totalTime += dt; diff --git a/source/src/Math/MathHelper.ts b/source/src/Math/MathHelper.ts index 9e616d3b..5298b4a8 100644 --- a/source/src/Math/MathHelper.ts +++ b/source/src/Math/MathHelper.ts @@ -25,7 +25,36 @@ module es { } /** - * mapps值(在leftMin - leftMax范围内)到rightMin - rightMax范围内的值 + * 返回由给定三角形和两个归一化重心(面积)坐标定义的点的一个轴的笛卡尔坐标 + * @param value1 + * @param value2 + * @param value3 + * @param amount1 + * @param amount2 + */ + public static barycentric(value1: number, value2: number, value3: number, amount1: number, amount2: number) { + return value1 + (value2 - value1) * amount1 + (value3 - value1) * amount2; + } + + /** + * 使用指定位置执行Catmull-Rom插值 + * @param value1 + * @param value2 + * @param value3 + * @param value4 + * @param amount + */ + public static catmullRom(value1: number, value2: number, value3: number, value4: number, amount: number) { + // 使用来自http://www.mvps.org/directx/articles/catmull/的公式 + let amountSquared = amount * amount; + let amountCubed = amountSquared * amount; + return (0.5 * (2 * value2 + (value3 - value1) * amount + + (2 * value1 - 5 * value2 + 4 * value3 - value4) * amountSquared + + (3 * value2 - value1 - 3 * value3 + value4) * amountCubed)); + } + + /** + * 将值(在leftMin-leftMax范围内)映射到一个在rightMin-rightMax范围内的值 * @param value * @param leftMin * @param leftMax @@ -36,6 +65,66 @@ module es { return rightMin + (value - leftMin) * (rightMax - rightMin) / (leftMax - leftMin); } + /** + * 将值从任意范围映射到0到1范围 + * @param value + * @param min + * @param max + * @returns + */ + public static map01(value: number, min: number, max: number) { + return (value - min) * 1 / (max - min); + } + + /** + * 将值从某个任意范围映射到1到0范围 + * 这相当于map01的取反 + * @param value + * @param min + * @param max + * @returns + */ + public static map10(value: number, min: number, max: number) { + return 1 - this.map01(value, min, max); + } + + /** + * 使用三次方程在两个值之间进行插值 + * @param value1 + * @param value2 + * @param amount + */ + public static smoothStep(value1: number, value2: number, amount: number) { + let result = this.clamp(amount, 0, 1); + result = MathHelper.hermite(value1, 0, value2, 0, result); + + return result; + } + + /** + * 将给定角度减小到π到-π之间的值 + * @param angle + */ + public static wrapAngle(angle: number) { + if ((angle > -Math.PI) && (angle <= Math.PI)) + return angle; + angle %= Math.PI * 2; + if (angle <= -Math.PI) + return angle + 2 * Math.PI; + if (angle > Math.PI) + return angle - 2 * Math.PI; + return angle; + } + + /** + * 确定值是否以2为底 + * @param value + * @returns + */ + public static isPowerOfTwo(value: number) { + return (value > 0) && ((value % (value - 1)) == 0); + } + public static lerp(from: number, to: number, t: number) { return from + (to - from) * this.clamp01(t); } @@ -99,18 +188,26 @@ module es { if (from < to) { if (t < from) return 0; - else if(t > to) + else if (t > to) return 1; } else { if (t < to) return 1; - else if(t > from) + else if (t > from) return 0; } return (t - from) / (to - from); } + /** + * 在两个值之间线性插值 + * 此方法是MathHelper.Lerp的效率较低,更精确的版本。 + */ + public static lerpPrecise(value1: number, value2: number, amount: number) { + return ((1 - amount) * value1) + (value2 * amount); + } + public static clamp(value: number, min: number, max: number) { if (value < min) return min; @@ -145,6 +242,27 @@ module es { return value % 2 == 0; } + /** + * 如果值是奇数,则返回true + * @param value + * @returns + */ + public static isOdd(value: number) { + return value % 2 != 0; + } + + /** + * 将值四舍五入并返回它和四舍五入后的数值 + * @param value + * @param roundedAmount + * @returns + */ + public static roundWithRoundedAmount(value: number, roundedAmount: Ref) { + let rounded = Math.round(value); + roundedAmount.value = value - (rounded * Math.round(value / rounded)); + return rounded; + } + /** * 数值限定在0-1之间 * @param value @@ -180,6 +298,41 @@ module es { return t; } + /** + * 递减t并确保其始终大于或等于0且小于长度 + * @param t + * @param length + * @returns + */ + public static decrementWithWrap(t: number, length: number) { + t--; + if (t < 0) + return length - 1; + + return t; + } + + /** + * 返回sqrt(x * x + y * y) + * @param x + * @param y + * @returns + */ + public static hypotenuse(x: number, y: number) { + return Math.sqrt(x * x + y * y); + } + + public static closestPowerOfTwoGreaterThan(x: number) { + x--; + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + + return (x + 1); + } + /** * 以roundToNearest为步长,将值舍入到最接近的数字。例如:在125中找到127到最近的5个结果 * @param value @@ -269,6 +422,17 @@ module es { return num; } + /** + * 检查值是否介于最小值/最大值(包括最小值/最大值)之间 + * @param value + * @param min + * @param max + * @returns + */ + public static between(value: number, min: number, max: number) { + return value >= min && value <= max; + } + /** * 计算以弧度为单位的两个给定角度之间的最短差 * @param current @@ -375,16 +539,56 @@ module es { * @param oscillationInterval * @returns */ - public static lissajouDamped(xFrequency: number = 2, yFrequency: number = 3, xMagnitude: number = 1, + public static lissajouDamped(xFrequency: number = 2, yFrequency: number = 3, xMagnitude: number = 1, yMagnitude: number = 1, phase: number = 0.5, damping: number = 0, oscillationInterval: number = 5) { - let wrappedTime = this.pingPong(Time.totalTime, oscillationInterval); - let damped = Math.pow(Math.E, -damping * wrappedTime); + let wrappedTime = this.pingPong(Time.totalTime, oscillationInterval); + let damped = Math.pow(Math.E, -damping * wrappedTime); - let x = damped * Math.sin(Time.totalTime * xFrequency + phase) * xMagnitude; - let y = damped * Math.cos(Time.totalTime * yFrequency) * yMagnitude; + let x = damped * Math.sin(Time.totalTime * xFrequency + phase) * xMagnitude; + let y = damped * Math.cos(Time.totalTime * yFrequency) * yMagnitude; - return new Vector2(x, y); + return new Vector2(x, y); + } + + /** + * 执行Hermite样条插值 + * @param value1 + * @param tangent1 + * @param value2 + * @param tangent2 + * @param amount + * @returns + */ + public static hermite(value1: number, tangent1: number, value2: number, tangent2: number, amount: number) { + let v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount, result; + let sCubed = s * s * s; + let sSquared = s * s; + + if (amount == 0) + result = value1; + else if (amount == 1) + result = value2; + else + result = (2 * v1 - 2 * v2 + t2 + t1) * sCubed + + (3 * v2 - 3 * v1 - 2 * t1 - t2) * sSquared + + t1 * s + + v1; + + return result; + } + + /** + * 此函数用于确保数不是NaN或无穷大 + * @param x + * @returns + */ + public static isValid(x: number) { + if (Number.isNaN(x)) { + return false; + } + + return !Number.isFinite(x); } } } diff --git a/source/src/Math/Vector2.ts b/source/src/Math/Vector2.ts index c27217e5..fefab83c 100644 --- a/source/src/Math/Vector2.ts +++ b/source/src/Math/Vector2.ts @@ -66,6 +66,19 @@ module es { return result; } + /** + * + * @param value1 + * @param value2 + * @returns + */ + public static multiplyScaler(value1: Vector2, value2: number) { + let result = new Vector2(0, 0); + result.x = value1.x * value2; + result.y = value1.x * value2; + return result; + } + /** * * @param value1 @@ -132,6 +145,18 @@ module es { return new Vector2(MathHelper.lerp(value1.x, value2.x, amount), MathHelper.lerp(value1.y, value2.y, amount)); } + /** + * 创建一个新的Vector2,其中包含指定矢量的线性插值 + * @param value1 + * @param value2 + * @param amount + * @returns + */ + public static lerpPrecise(value1: Vector2, value2: Vector2, amount: number) { + return new Vector2(MathHelper.lerpPrecise(value1.x, value2.x, amount), + MathHelper.lerpPrecise(value1.y, value2.y, amount)); + } + /** * 创建一个新的Vector2,该Vector2包含了通过指定的Matrix进行的二维向量变换。 * @param position @@ -142,6 +167,16 @@ module es { (position.x * matrix.m12) + (position.y * matrix.m22) + matrix.m32); } + /** + * 创建一个新的Vector2,其中包含由指定的Matrix转换的指定法线 + * @param normal + * @param matrix + */ + public static transformNormal(normal: Vector2, matrix: Matrix) { + return new Vector2((normal.x * matrix.m11) + (normal.y * matrix.m21), + (normal.x * matrix.m12) + (normal.y * matrix.m22)); + } + /** * 返回两个向量之间的距离 * @param value1 @@ -176,6 +211,32 @@ module es { return value; } + /** + * 创建一个新的Vector2,其中包含给定矢量和法线的反射矢量 + * @param vector + * @param normal + * @returns + */ + public static reflect(vector: Vector2, normal: Vector2) { + let result: Vector2 = new Vector2(); + let val = 2 * ((vector.x * normal.x) + (vector.y * normal.y)); + result.x = vector.x - (normal.x * val); + result.y = vector.y - (normal.y * val); + return result; + } + + /** + * 创建一个新的Vector2,其中包含指定矢量的三次插值 + * @param value1 + * @param value2 + * @param amount + * @returns + */ + public static smoothStep(value1: Vector2, value2: Vector2, amount: number) { + return new Vector2(MathHelper.smoothStep(value1.x, value2.x, amount), + MathHelper.smoothStep(value1.y, value2.y, amount)); + } + /** * * @param value @@ -206,6 +267,17 @@ module es { return this; } + /** + * + * @param value + * @returns + */ + public multiplyScaler(value: number): Vector2 { + this.x *= value; + this.y *= value; + return this; + } + /** * 从当前Vector2减去一个Vector2 * @param value 要减去的Vector2 @@ -270,6 +342,46 @@ module es { return false; } + public isValid(): boolean { + return MathHelper.isValid(this.x) && MathHelper.isValid(this.y); + } + + /** + * 创建一个新的Vector2,其中包含来自两个向量的最小值 + * @param value1 + * @param value2 + * @returns + */ + public static min(value1: Vector2, value2: Vector2) { + return new Vector2(value1.x < value2.x ? value1.x : value2.x, + value1.y < value2.y ? value1.y : value2.y); + } + + /** + * 创建一个新的Vector2,其中包含两个向量的最大值 + * @param value1 + * @param value2 + * @returns + */ + public static max(value1: Vector2, value2: Vector2) { + return new Vector2(value1.x > value2.x ? value1.x : value2.x, + value1.y > value2.y ? value1.y : value2.y); + } + + /** + * 创建一个新的Vector2,其中包含Hermite样条插值 + * @param value1 + * @param tangent1 + * @param value2 + * @param tangent2 + * @param amount + * @returns + */ + public static hermite(value1: Vector2, tangent1: Vector2, value2: Vector2, tangent2: Vector2, amount: number){ + return new Vector2(MathHelper.hermite(value1.x, tangent1.x, value2.x, tangent2.x, amount), + MathHelper.hermite(value1.y, tangent1.y, value2.y, tangent2.y, amount)); + } + public clone(): Vector2 { return new Vector2(this.x, this.y); } diff --git a/source/src/Utils/Extensions/RectangleExt.ts b/source/src/Utils/Extensions/RectangleExt.ts index 310de0c7..64aaf7bc 100644 --- a/source/src/Utils/Extensions/RectangleExt.ts +++ b/source/src/Utils/Extensions/RectangleExt.ts @@ -104,7 +104,7 @@ module es { let maxX = Number.NEGATIVE_INFINITY; let maxY = Number.NEGATIVE_INFINITY; - for (let i = 0; i < points.length; i ++) { + for (let i = 0; i < points.length; i++) { let pt = points[i]; if (pt.x < minX) @@ -136,7 +136,7 @@ module es { * @param deltaX * @param deltaY */ - public static getSweptBroadphaseBounds(rect: Rectangle, deltaX: number, deltaY: number){ + public static getSweptBroadphaseBounds(rect: Rectangle, deltaX: number, deltaY: number) { let broadphasebox = Rectangle.empty; broadphasebox.x = deltaX > 0 ? rect.x : rect.x + deltaX; @@ -216,5 +216,134 @@ module es { return new Vector2(depthX, depthY); } + + public static getClosestPointOnBoundsToOrigin(rect: Rectangle) { + let max = this.getMax(rect); + let minDist = Math.abs(rect.location.x); + let boundsPoint = new Vector2(rect.location.x, 0); + + if (Math.abs(max.x) < minDist) { + minDist = Math.abs(max.x); + boundsPoint.x = max.x; + boundsPoint.y = 0; + } + + if (Math.abs(max.y) < minDist) { + minDist = Math.abs(max.y); + boundsPoint.x = 0; + boundsPoint.y = max.y; + } + + if (Math.abs(rect.location.y) < minDist) { + minDist = Math.abs(rect.location.y); + boundsPoint.x = 0; + boundsPoint.y = rect.location.y; + } + + return boundsPoint; + } + + /** + * 将Rectangle中或上的最接近点返回给定点 + * @param rect + * @param point + */ + public static getClosestPointOnRectangleToPoint(rect: Rectangle, point: Vector2) { + // 对于每个轴,如果该点在盒子外面,则将在盒子上,否则不理会它 + let res = new Vector2(); + res.x = MathHelper.clamp(point.x, rect.left, rect.right) + res.y = MathHelper.clamp(point.y, rect.top, rect.bottom); + + return res; + } + + /** + * 获取矩形边界上与给定点最接近的点 + * @param rect + * @param point + */ + public static getClosestPointOnRectangleBorderToPoint(rect: Rectangle, point: Vector2) { + // 对于每个轴,如果该点在盒子外面,则将在盒子上,否则不理会它 + let res = new Vector2(); + res.x = MathHelper.clamp(point.x, rect.left, rect.right) + res.y = MathHelper.clamp(point.y, rect.top, rect.bottom); + + // 如果点在矩形内,我们需要将res推到边框,因为它将在矩形内 + if (rect.contains(res.x, res.y)) { + let dl = rect.x - rect.left; + let dr = rect.right - res.x; + let dt = res.y - rect.top; + let db = rect.bottom - res.y; + + let min = Math.min(dl, dr, dt, db); + if (min == dt) + res.y = rect.top; + else if (min == db) + res.y = rect.bottom; + else if (min == dl) + res.x == rect.left; + else + res.x = rect.right; + } + + return res; + } + + public static getMax(rect: Rectangle) { + return new Vector2(rect.right, rect.bottom); + } + + /** + * 以Vector2的形式获取矩形的中心点 + * @param rect + * @returns + */ + public static getCenter(rect: Rectangle) { + return new Vector2(rect.x + rect.width / 2, rect.y + rect.height / 2); + } + + /** + * 给定多边形的点即可计算边界 + * @param points + */ + public static boundsFromPolygonPoints(points: Vector2[]) { + // 我们需要找到最小/最大x / y值 + let minX = Number.POSITIVE_INFINITY; + let minY = Number.POSITIVE_INFINITY; + let maxX = Number.NEGATIVE_INFINITY; + let maxY = Number.NEGATIVE_INFINITY; + + for (let i = 0; i < points.length; i++) { + let pt = points[i]; + + if (pt.x < minX) + minX = pt.x; + if (pt.x > maxX) + maxX = pt.x + + if (pt.y < minY) + minY = pt.y; + if (pt.y > maxY) + maxY = pt.y; + } + + return this.fromMinMaxVector(new Vector2(minX, minY), new Vector2(maxX, maxY)); + } + + /** + * 缩放矩形 + * @param rect + * @param scale + */ + public static scale(rect: Rectangle, scale: Vector2) { + rect.x = rect.x * scale.x; + rect.y = rect.y * scale.y; + rect.width = rect.width * scale.x; + rect.height = rect.height * scale.y; + } + + public static translate(rect: Rectangle, vec: Vector2) { + rect.location.add(vec); + } } } diff --git a/source/src/Utils/Extensions/Vector2Ext.ts b/source/src/Utils/Extensions/Vector2Ext.ts index 365043ba..658487a3 100644 --- a/source/src/Utils/Extensions/Vector2Ext.ts +++ b/source/src/Utils/Extensions/Vector2Ext.ts @@ -51,6 +51,18 @@ module es { return Math.acos(MathHelper.clamp(Vector2.dot(from, to), -1, 1)) * MathHelper.Rad2Deg; } + /** + * 返回以自度为中心的左右角度 + * @param self + * @param left + * @param right + */ + public static angleBetween(self: Vector2, left: Vector2, right: Vector2) { + let one = Vector2.subtract(left, self); + let two = Vector2.subtract(right, self); + return this.angle(one, two); + } + /** * 给定两条直线(ab和cd),求交点 * @param a