diff --git a/source/src/ECS/Component.ts b/source/src/ECS/Component.ts index 04fe8007..2959113e 100644 --- a/source/src/ECS/Component.ts +++ b/source/src/ECS/Component.ts @@ -7,7 +7,7 @@ module es { * 删除执行顺序 * - onRemovedFromEntity */ - export abstract class Component { + export abstract class Component extends egret.HashObject { /** * 此组件附加的实体 */ diff --git a/source/src/Extension.ts b/source/src/Extension.ts index e3f9b865..6d0141c1 100644 --- a/source/src/Extension.ts +++ b/source/src/Extension.ts @@ -3,55 +3,55 @@ declare interface Array { * 获取满足表达式的数组元素索引 * @param predicate 表达式 */ - findIndex(predicate: Function): number; + findIndex(predicate: (c: T)=>boolean): number; /** * 是否存在满足表达式的数组元素 * @param predicate 表达式 */ - any(predicate: Function): boolean; + any(predicate: (c: T) => boolean): boolean; /** * 获取满足表达式的第一个或默认数组元素 * @param predicate 表达式 */ - firstOrDefault(predicate: Function): T; + firstOrDefault(predicate: (c: T)=>boolean): T; /** * 获取满足表达式的第一个数组元素 * @param predicate 表达式 */ - find(predicate: Function): T; + find(predicate: (c: T) => boolean): T; /** * 筛选满足表达式的数组元素 * @param predicate 表达式 */ - where(predicate: Function): Array; + where(predicate: (c: T) => boolean): Array; /** * 获取满足表达式的数组元素的计数 * @param predicate 表达式 */ - count(predicate: Function): number; + count(predicate: (c: T) => boolean): number; /** * 获取满足表达式的数组元素的数组 * @param predicate 表达式 */ - findAll(predicate: Function): Array; + findAll(predicate: (c: T) => boolean): Array; /** * 是否有获取满足表达式的数组元素 * @param value 值 */ - contains(value): boolean; + contains(value: T): boolean; /** * 移除满足表达式的数组元素 * @param predicate 表达式 */ - removeAll(predicate: Function): void; + removeAll(predicate: (c: T) => boolean): void; /** * 移除数组元素 @@ -63,14 +63,14 @@ declare interface Array { * 移除特定索引数组元素 * @param index 索引 */ - removeAt(index): void; + removeAt(index: number): void; /** * 移除范围数组元素 * @param index 开始索引 * @param count 删除的个数 */ - removeRange(index, count): void; + removeRange(index: number, count: number): void; /** * 获取通过选择器转换的数组 @@ -102,7 +102,7 @@ declare interface Array { * 求和 * @param selector 选择器 */ - sum(selector); + sum(selector: Function): number; } Array.prototype.findIndex = function (predicate) { @@ -189,6 +189,11 @@ Array.prototype.findAll = function (predicate) { Array.prototype.contains = function (value) { function contains(array, value) { for (let i = 0, len = array.length; i < len; i++) { + if (array[i] instanceof egret.HashObject && value instanceof egret.HashObject){ + if ((array[i] as egret.HashObject).hashCode == (value as egret.HashObject).hashCode) + return true; + } + if (array[i] == value) { return true; } diff --git a/source/src/Math/Rectangle.ts b/source/src/Math/Rectangle.ts index d18c2d3a..6cab79b3 100644 --- a/source/src/Math/Rectangle.ts +++ b/source/src/Math/Rectangle.ts @@ -81,6 +81,51 @@ module es { this.top < value.bottom; } + public rayIntersects(ray: Ray2D): number{ + let distance = 0; + let maxValue = Number.MAX_VALUE; + + if (Math.abs(ray.direction.x) < 1E-06){ + if ((ray.start.x < this.x) || (ray.start.x > this.x + this.width)) + return distance; + }else{ + let num11 = 1 / ray.direction.x; + let num8 = (this.x - ray.start.x) * num11; + let num7 = (this.x + this.width - ray.start.x) * num11; + if (num8 > num7){ + let num14 = num8; + num8 = num7; + num7 = num14; + } + + distance = Math.max(num8, distance); + maxValue = Math.min(num7, maxValue); + if (distance > maxValue) + return distance; + } + + if (Math.abs(ray.direction.y) < 1E-06){ + if ((ray.start.y < this.y) || (ray.start.y > this.y + this.height)) + return distance; + }else{ + let num10 = 1 / ray.direction.y; + let num6 = (this.y - ray.start.y) * num10; + let num5 = (this.y + this.height - ray.start.y) * num10; + if (num6 > num5){ + let num13 = num6; + num6 = num5; + num5 = num13; + } + + distance = Math.max(num6, distance); + maxValue = Math.max(num5, maxValue); + if (distance > maxValue) + return distance; + } + + return distance; + } + /** * 获取所提供的矩形是否在此矩形的边界内 * @param value diff --git a/source/src/Physics/Physics.ts b/source/src/Physics/Physics.ts index 05b17fed..8c1867be 100644 --- a/source/src/Physics/Physics.ts +++ b/source/src/Physics/Physics.ts @@ -5,6 +5,10 @@ module es { /** 接受layerMask的所有方法的默认值 */ public static readonly allLayers: number = -1; private static _spatialHash: SpatialHash; + /** + * raycast是否检测配置为触发器的碰撞器 + */ + public static raycastsHitTriggers: boolean = false; public static reset() { this._spatialHash = new SpatialHash(this.spatialHashCellSize); diff --git a/source/src/Physics/Ray2D.ts b/source/src/Physics/Ray2D.ts new file mode 100644 index 00000000..9a2043ce --- /dev/null +++ b/source/src/Physics/Ray2D.ts @@ -0,0 +1,16 @@ +module es { + /** + * 不是真正的射线(射线只有开始和方向),作为一条线和射线。 + */ + export class Ray2D { + public start: Vector2; + public end: Vector2; + public direction: Vector2; + + constructor(position: Vector2, end: Vector2){ + this.start = position; + this.end = end; + this.direction = Vector2.subtract(this.end, this.start); + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Shapes/RealtimeCollisions.ts b/source/src/Physics/Shapes/RealtimeCollisions.ts new file mode 100644 index 00000000..47314dc5 --- /dev/null +++ b/source/src/Physics/Shapes/RealtimeCollisions.ts @@ -0,0 +1,48 @@ +module es { + export class RealtimeCollisions { + public static intersectMovingCircleToBox(s: Circle, b: Box, movement: Vector2): number { + // 计算用球面半径r inflate b得到的AABB + let e = b.bounds; + e.inflate(s.radius, s.radius); + + // 射线与展开矩形e相交。如果射线错过了e,则退出不相交,否则得到相交点p和时间t + let ray = new Ray2D(Vector2.subtract(s.position, movement), s.position); + let time = e.rayIntersects(ray); + if (time > 1) + return time; + + // 求交点 + let point = Vector2.add(ray.start, Vector2.add(ray.direction, new Vector2(time))); + + // 计算b的最小面和最大面p的交点在哪个面之外。注意,u和v不能有相同的位集,它们之间必须至少有一个位集。 + let u, v = 0; + if (point.x < b.bounds.left) + u |= 1; + if (point.x > b.bounds.right) + v |= 1; + if (point.y < b.bounds.top) + u |= 2; + if (point.y > b.bounds.bottom) + v |= 2; + + // 将所有位集合成位掩码(注意u + v == u | v) + let m = u + v; + + // 如果所有的3位都被设置,那么点在一个顶点区域 + if (m == 3){ + // 现在必须相交的部分,如果一个或多个击中对胶囊的两边会合在斜面和返回的最佳时间 + // TODO: 需要实现这个 + console.log(`m == 3. corner ${Time.frameCount}`); + } + + // 如果m中只设置了一个位,那么点在一个面区域 + if ((m & (m - 1)) == 0){ + // 什么也不做。从扩展矩形交集的时间是正确的时间 + return time; + } + + // 点在边缘区域上。与边缘相交。 + return time; + } + } +} \ No newline at end of file diff --git a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisions.ts b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisions.ts index ebf4d570..451c61fb 100644 --- a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisions.ts +++ b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisions.ts @@ -1,4 +1,9 @@ module es { + /** + * 各种形状的碰撞例程 + * 大多数人都希望第一个形状位于第二个形状的空间内(即shape1) + * pos应该设置为shape1。pos - shape2.pos)。 + */ export class ShapeCollisions { /** * 检查两个多边形之间的碰撞 @@ -306,5 +311,45 @@ module es { return new Rectangle(topLeft.x, topLeft.y, fullSize.x, fullSize.y) } + + /** + * 用second检查被deltaMovement移动的框的结果 + * @param first + * @param second + * @param movement + * @param hit + */ + public static boxToBoxCast(first: Box, second: Box, movement: Vector2, hit: RaycastHit): boolean{ + // 首先,我们检查是否有重叠。如果有重叠,我们就不做扫描测试 + let minkowskiDiff = this.minkowskiDifference(first, second); + if (minkowskiDiff.contains(0, 0)){ + // 计算MTV。如果它是零,我们就可以称它为非碰撞 + let mtv = minkowskiDiff.getClosestPointOnBoundsToOrigin(); + if (mtv.equals(Vector2.zero)) + return false; + + hit.normal = new Vector2(-mtv.x); + hit.normal = hit.normal.normalize(); + hit.distance = 0; + hit.fraction = 0; + + return true; + }else{ + // 射线投射移动矢量 + let ray = new Ray2D(Vector2.zero, new Vector2(-movement.x)); + let fraction: number = minkowskiDiff.rayIntersects(ray); + if (fraction <= 1){ + hit.fraction = fraction; + hit.distance = movement.length() * fraction; + hit.normal = new Vector2(-movement.x); + hit.normal = hit.normal.normalize(); + hit.centroid = Vector2.add(first.bounds.center, Vector2.multiply(movement, new Vector2(fraction))); + + return true; + } + } + + return false; + } } } diff --git a/source/src/Physics/Verlet/SpatialHash.ts b/source/src/Physics/Verlet/SpatialHash.ts index 22b5ca7e..89ba89fb 100644 --- a/source/src/Physics/Verlet/SpatialHash.ts +++ b/source/src/Physics/Verlet/SpatialHash.ts @@ -52,8 +52,8 @@ module es { for (let x = p1.x; x <= p2.x; x++) { for (let y = p1.y; y <= p2.y; y++) { // 如果没有单元格,我们需要创建它 - let c = this.cellAtPosition(x, y, true); - if (c.indexOf(collider) == -1) + let c: Collider[] = this.cellAtPosition(x, y, true); + if (!c.firstOrDefault(c => c.hashCode == collider.hashCode)) c.push(collider); } } @@ -134,7 +134,7 @@ module es { continue; if (bounds.intersects(collider.bounds)) { - if (this._tempHashSet.indexOf(collider) == -1) + if (!this._tempHashSet.firstOrDefault(c => c.hashCode == collider.hashCode)) this._tempHashSet.push(collider); } } @@ -202,7 +202,7 @@ module es { * @param y * @param createCellIfEmpty */ - private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false) { + private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false): Collider[] { let cell: Collider[] = this._cellDict.tryGetValue(x, y); if (!cell) { if (createCellIfEmpty) { @@ -262,6 +262,55 @@ module es { } export class RaycastResultParser { + public hitCounter: number; + public static compareRaycastHits = (a: RaycastHit, b: RaycastHit) => { + return a.distance - b.distance; + }; + public _hits: RaycastHit[]; + public _tempHit: RaycastHit; + public _checkedColliders: Collider[] = []; + public _cellHits: RaycastHit[] = []; + public _ray: Ray2D; + public _layerMask: number; + + public start(ray: Ray2D, hits: RaycastHit[], layerMask: number){ + this._ray = ray; + this._hits = hits; + this._layerMask = layerMask; + this.hitCounter = 0; + } + + /** + * 如果hits数组被填充,返回true。单元格不能为空! + * @param cellX + * @param cellY + * @param cell + */ + public checkRayIntersection(cellX: number, cellY: number, cell: Collider[]): boolean{ + let fraction: number; + for (let i = 0; i < cell.length; i ++) { + let potential = cell[i]; + + // 管理我们已经处理过的碰撞器 + if (this._checkedColliders.contains(potential)) + continue; + + this._checkedColliders.push(potential); + // 只有当我们被设置为这样做时才会点击触发器 + if (potential.isTrigger && !Physics.raycastsHitTriggers) + continue; + + // 确保碰撞器在图层蒙版上 + if (!Flags.isFlagSet(this._layerMask, potential.physicsLayer)) + continue; + + // TODO: rayIntersects的性能够吗?需要测试它。Collisions.rectToLine可能更快 + // TODO: 如果边界检查返回更多数据,我们就不需要为BoxCollider检查做任何事情 + // 在做形状测试之前先做一个边界检查 + let colliderBounds = potential.bounds; + if (colliderBounds.) + } + } } }