From 814234ca610c681714cf424a6ff5b2351732518e Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Thu, 23 Jul 2020 09:10:27 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E7=B1=BB=E8=87=B3es=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/src/ECS/Components/ComponentPool.ts | 38 +- source/src/ECS/Components/Mesh.ts | 48 +- .../Physics/Colliders/BoxCollider.ts | 147 +++--- .../Physics/Colliders/CircleCollider.ts | 83 ++-- .../Physics/Colliders/PolygonCollider.ts | 36 +- .../Components/Physics/ITriggerListener.ts | 27 +- source/src/ECS/Components/Physics/Mover.ts | 152 +++--- .../ECS/Components/Physics/ProjectileMover.ts | 96 ++-- .../ECS/Components/ScrollingSpriteRenderer.ts | 68 +-- source/src/ECS/Components/Sprite.ts | 42 +- source/src/ECS/Components/SpriteAnimation.ts | 16 +- source/src/ECS/Components/SpriteAnimator.ts | 246 +++++----- source/src/ECS/Components/SpriteRenderer.ts | 24 +- .../src/ECS/Components/TiledSpriteRenderer.ts | 108 ++--- source/src/ECS/Utils/BitSet.ts | 222 ++++----- source/src/ECS/Utils/ComponentTypeManager.ts | 30 +- source/src/ECS/Utils/EntityProcessorList.ts | 132 ++--- source/src/Physics/Shapes/Box.ts | 23 +- source/src/Physics/Shapes/Circle.ts | 103 ++-- source/src/Physics/Verlet/SpatialHash.ts | 458 +++++++++--------- 20 files changed, 1101 insertions(+), 998 deletions(-) diff --git a/source/src/ECS/Components/ComponentPool.ts b/source/src/ECS/Components/ComponentPool.ts index df1d66f2..bcac36fb 100644 --- a/source/src/ECS/Components/ComponentPool.ts +++ b/source/src/ECS/Components/ComponentPool.ts @@ -1,22 +1,24 @@ -class ComponentPool{ - private _cache: T[]; - private _type: any; +module es { + export class ComponentPool{ + private _cache: T[]; + private _type: any; - constructor(typeClass: any){ - this._type = typeClass; - this._cache = []; - } + constructor(typeClass: any){ + this._type = typeClass; + this._cache = []; + } - public obtain(): T{ - try { - return this._cache.length > 0 ? this._cache.shift() : new this._type(); - } catch(err){ - throw new Error(this._type + err); + public obtain(): T{ + try { + return this._cache.length > 0 ? this._cache.shift() : new this._type(); + } catch(err){ + throw new Error(this._type + err); + } + } + + public free(component: T){ + component.reset(); + this._cache.push(component); } } - - public free(component: T){ - component.reset(); - this._cache.push(component); - } -} \ No newline at end of file +} diff --git a/source/src/ECS/Components/Mesh.ts b/source/src/ECS/Components/Mesh.ts index bea8a9c9..211d50e2 100644 --- a/source/src/ECS/Components/Mesh.ts +++ b/source/src/ECS/Components/Mesh.ts @@ -1,32 +1,24 @@ /// -class Mesh extends RenderableComponent { - private _mesh: egret.Mesh; +module es { + export class Mesh extends RenderableComponent { + private _mesh: egret.Mesh; - constructor(){ - super(); + constructor(){ + super(); - this._mesh = new egret.Mesh(); + this._mesh = new egret.Mesh(); + } + + public setTexture(texture: egret.Texture): Mesh{ + this._mesh.texture = texture; + + return this; + } + + public reset() { + } + + render(camera: es.Camera) { + } } - - public setTexture(texture: egret.Texture): Mesh{ - this._mesh.texture = texture; - - return this; - } - - public onAddedToEntity(){ - this.addChild(this._mesh); - } - - public onRemovedFromEntity(){ - this.removeChild(this._mesh); - } - - public render(camera: Camera){ - this.x = this.entity.position.x - camera.position.x + camera.origin.x; - this.y = this.entity.position.y - camera.position.y + camera.origin.y; - } - - public reset() { - } -} \ No newline at end of file +} diff --git a/source/src/ECS/Components/Physics/Colliders/BoxCollider.ts b/source/src/ECS/Components/Physics/Colliders/BoxCollider.ts index 14e56800..504d59f3 100644 --- a/source/src/ECS/Components/Physics/Colliders/BoxCollider.ts +++ b/source/src/ECS/Components/Physics/Colliders/BoxCollider.ts @@ -1,74 +1,85 @@ /// -class BoxCollider extends Collider { - public get width(){ - return (this.shape as Box).width; - } - - public set width(value: number){ - this.setWidth(value); - } - - /** - * 设置BoxCollider的宽度 - * @param width - */ - public setWidth(width: number): BoxCollider{ - this._colliderRequiresAutoSizing = false; - let box = this.shape as Box; - if (width != box.width){ - // 更新框,改变边界,如果我们需要更新物理系统中的边界 - box.updateBox(width, box.height); - if (this.entity && this._isParentEntityAddedToScene) - Physics.updateCollider(this); +module es { + export class BoxCollider extends Collider { + public get width(){ + return (this.shape as Box).width; } - return this; - } - - public get height(){ - return (this.shape as Box).height; - } - - public set height(value: number){ - this.setHeight(value); - } - - /** - * 设置BoxCollider的高度 - * @param height - */ - public setHeight(height: number){ - this._colliderRequiresAutoSizing = false; - let box = this.shape as Box; - if (height != box.height){ - // 更新框,改变边界,如果我们需要更新物理系统中的边界 - box.updateBox(box.width, height); - if (this.entity && this._isParentEntityAddedToScene) - Physics.updateCollider(this); - } - } - - /** - * 零参数构造函数要求RenderableComponent在实体上,这样碰撞器可以在实体被添加到场景时调整自身的大小。 - */ - constructor(){ - super(); - - // 我们在这里插入一个1x1框作为占位符,直到碰撞器在下一阵被添加到实体并可以获得更精确的自动调整大小数据 - this.shape = new Box(1, 1); - this._colliderRequiresAutoSizing = true; - } - - public setSize(width: number, height: number){ - this._colliderRequiresAutoSizing = false; - let box = this.shape as Box; - if (width != box.width || height != box.height){ - // 更新框,改变边界,如果我们需要更新物理系统中的边界 - box.updateBox(width, height); - if (this.entity && this._isParentEntityAddedToScene) - Physics.updateCollider(this); + public set width(value: number){ + this.setWidth(value); } - return this; + public get height(){ + return (this.shape as Box).height; + } + + public set height(value: number){ + this.setHeight(value); + } + + /** + * 零参数构造函数要求RenderableComponent在实体上,这样碰撞器可以在实体被添加到场景时调整自身的大小。 + */ + constructor(){ + super(); + + // 我们在这里插入一个1x1框作为占位符,直到碰撞器在下一阵被添加到实体并可以获得更精确的自动调整大小数据 + this.shape = new Box(1, 1); + this._colliderRequiresAutoSizing = true; + } + + /** + * 设置BoxCollider的大小 + * @param width + * @param height + */ + public setSize(width: number, height: number){ + this._colliderRequiresAutoSizing = false; + let box = this.shape as Box; + if (width != box.width || height != box.height){ + // 更新框,改变边界,如果我们需要更新物理系统中的边界 + box.updateBox(width, height); + if (this.entity && this._isParentEntityAddedToScene) + Physics.updateCollider(this); + } + + return this; + } + + /** + * 设置BoxCollider的宽度 + * @param width + */ + public setWidth(width: number): BoxCollider{ + this._colliderRequiresAutoSizing = false; + let box = this.shape as Box; + if (width != box.width){ + // 更新框,改变边界,如果我们需要更新物理系统中的边界 + box.updateBox(width, box.height); + if (this.entity && this._isParentEntityAddedToScene) + Physics.updateCollider(this); + } + + return this; + } + + /** + * 设置BoxCollider的高度 + * @param height + */ + public setHeight(height: number){ + this._colliderRequiresAutoSizing = false; + let box = this.shape as Box; + if (height != box.height){ + // 更新框,改变边界,如果我们需要更新物理系统中的边界 + box.updateBox(box.width, height); + if (this.entity && this._isParentEntityAddedToScene) + Physics.updateCollider(this); + } + } + + public toString(){ + return `[BoxCollider: bounds: ${this.bounds}]`; + } } -} \ No newline at end of file +} diff --git a/source/src/ECS/Components/Physics/Colliders/CircleCollider.ts b/source/src/ECS/Components/Physics/Colliders/CircleCollider.ts index f5d4bb05..26df381b 100644 --- a/source/src/ECS/Components/Physics/Colliders/CircleCollider.ts +++ b/source/src/ECS/Components/Physics/Colliders/CircleCollider.ts @@ -1,41 +1,48 @@ -class CircleCollider extends Collider { - public get radius(): number{ - return (this.shape as Circle).radius; - } - public set radius(value: number){ - this.setRadius(value); - } - - /** - * 创建一个有半径的圆 - * - * @param radius - */ - constructor(radius?: number){ - super(); - - if (radius) - this._colliderRequiresAutoSizing = true; - // 我们在这里插入一个1px的圆圈作为占位符 - // 直到碰撞器被添加到实体并可以获得更精确的自动调整大小数据的下一帧 - this.shape = new Circle(radius ? radius : 1); - } - - /** - * 设置圆的半径 - * @param radius - */ - public setRadius(radius: number): CircleCollider{ - this._colliderRequiresAutoSizing = false; - let circle = this.shape as Circle; - if (radius != circle.radius){ - circle.radius = radius; - circle._originalRadius = radius; - - if (this.entity && this._isParentEntityAddedToScene) - Physics.updateCollider(this); +module es { + export class CircleCollider extends Collider { + public get radius(): number { + return (this.shape as Circle).radius; } - return this; + public set radius(value: number) { + this.setRadius(value); + } + + /** + * 创建一个有半径的圆 + * + * @param radius + */ + constructor(radius?: number) { + super(); + + if (radius) + this._colliderRequiresAutoSizing = true; + // 我们在这里插入一个1px的圆圈作为占位符 + // 直到碰撞器被添加到实体并可以获得更精确的自动调整大小数据的下一帧 + this.shape = new Circle(radius ? radius : 1); + } + + /** + * 设置圆的半径 + * @param radius + */ + public setRadius(radius: number): CircleCollider { + this._colliderRequiresAutoSizing = false; + let circle = this.shape as Circle; + if (radius != circle.radius) { + circle.radius = radius; + circle._originalRadius = radius; + + if (this.entity && this._isParentEntityAddedToScene) + Physics.updateCollider(this); + } + + return this; + } + + public toString() { + return `[CircleCollider: bounds: ${this.bounds}, radius: ${(this.shape as Circle).radius}]` + } } -} \ No newline at end of file +} diff --git a/source/src/ECS/Components/Physics/Colliders/PolygonCollider.ts b/source/src/ECS/Components/Physics/Colliders/PolygonCollider.ts index e0a934f2..9e6732f4 100644 --- a/source/src/ECS/Components/Physics/Colliders/PolygonCollider.ts +++ b/source/src/ECS/Components/Physics/Colliders/PolygonCollider.ts @@ -1,22 +1,26 @@ -/** - * 多边形应该以顺时针方式定义 - */ -class PolygonCollider extends Collider { +module es { /** - * 如果这些点没有居中,它们将以localOffset的差异为居中。 - * @param points + * 多边形应该以顺时针方式定义 */ - constructor(points: Vector2[]){ - super(); + export class PolygonCollider extends Collider { + /** + * 如果这些点没有居中,它们将以localOffset的差异为居中。 + * @param points + */ + constructor(points: Vector2[]) { + super(); - // 第一点和最后一点决不能相同。我们想要一个开放的多边形 - let isPolygonClosed = points[0] == points[points.length - 1]; + // 第一点和最后一点决不能相同。我们想要一个开放的多边形 + let isPolygonClosed = points[0] == points[points.length - 1]; - // 最后一个移除 - if (isPolygonClosed) - points.splice(points.length - 1, 1); + // 最后一个移除 + if (isPolygonClosed) + points.splice(points.length - 1, 1); - Polygon.recenterPolygonVerts(points); - this.shape = new Polygon(points); + let center = Polygon.findPolygonCenter(points); + this.setLocalOffset(center); + Polygon.recenterPolygonVerts(points); + this.shape = new Polygon(points); + } } -} \ No newline at end of file +} diff --git a/source/src/ECS/Components/Physics/ITriggerListener.ts b/source/src/ECS/Components/Physics/ITriggerListener.ts index a04f150c..dd7e2ff0 100644 --- a/source/src/ECS/Components/Physics/ITriggerListener.ts +++ b/source/src/ECS/Components/Physics/ITriggerListener.ts @@ -1,4 +1,23 @@ -interface ITriggerListener { - onTriggerEnter(other: Collider, local: Collider); - onTriggerExit(other: Collider, local: Collider); -} \ No newline at end of file +module es{ + /** + * 当添加到组件时,每当实体上的冲突器与另一个组件重叠/退出时,将调用这些方法。 + * ITriggerListener方法将在实现接口的触发器实体上的任何组件上调用。 + * 注意,这个接口只与Mover类一起工作 + */ + export interface ITriggerListener { + /** + * 当碰撞器与触发碰撞器相交时调用。这是在触发碰撞器和触发碰撞器上调用的。 + * 移动必须由Mover/ProjectileMover方法处理,以使其自动工作。 + * @param other + * @param local + */ + onTriggerEnter(other: Collider, local: Collider); + + /** + * 当另一个碰撞器离开触发碰撞器时调用 + * @param other + * @param local + */ + onTriggerExit(other: Collider, local: Collider); + } +} diff --git a/source/src/ECS/Components/Physics/Mover.ts b/source/src/ECS/Components/Physics/Mover.ts index f1acc291..0de3dba3 100644 --- a/source/src/ECS/Components/Physics/Mover.ts +++ b/source/src/ECS/Components/Physics/Mover.ts @@ -1,93 +1,95 @@ -/** - * 辅助类说明了一种处理移动的方法,它考虑了包括触发器在内的所有冲突。 - * ITriggerListener接口用于管理对移动过程中违反的任何触发器的回调。 - * 一个物体只能通过移动器移动。要正确报告触发器的move方法。 - * - * 请注意,多个移动者相互交互将多次调用ITriggerListener。 - */ -class Mover extends Component { - private _triggerHelper: ColliderTriggerHelper; - - public onAddedToEntity(){ - this._triggerHelper = new ColliderTriggerHelper(this.entity); - } - +module es { /** - * 计算修改运动矢量的运动,以考虑移动时可能发生的碰撞 - * @param motion + * 辅助类说明了一种处理移动的方法,它考虑了包括触发器在内的所有冲突。 + * ITriggerListener接口用于管理对移动过程中违反的任何触发器的回调。 + * 一个物体只能通过移动器移动。要正确报告触发器的move方法。 + * + * 请注意,多个移动者相互交互将多次调用ITriggerListener。 */ - public calculateMovement(motion: Vector2){ - let collisionResult = new CollisionResult(); + export class Mover extends Component { + private _triggerHelper: ColliderTriggerHelper; - if (!this.entity.getComponent(Collider) || !this._triggerHelper){ - return null; + public onAddedToEntity(){ + this._triggerHelper = new ColliderTriggerHelper(this.entity); } - // 移动所有的非触发碰撞器并获得最近的碰撞 - let colliders: Collider[] = this.entity.getComponents(Collider); - for (let i = 0; i < colliders.length; i ++){ - let collider = colliders[i]; + /** + * 计算修改运动矢量的运动,以考虑移动时可能发生的碰撞 + * @param motion + */ + public calculateMovement(motion: Vector2){ + let collisionResult = new CollisionResult(); - // 不检测触发器 在我们移动后会重新访问它 - if (collider.isTrigger) - continue; + if (!this.entity.getComponent(Collider) || !this._triggerHelper){ + return null; + } - // 获取我们在新位置可能发生碰撞的任何东西 - let bounds = collider.bounds; - bounds.x += motion.x; - bounds.y += motion.y; - let boxcastResult = Physics.boxcastBroadphaseExcludingSelf(collider, bounds, collider.collidesWithLayers); - bounds = boxcastResult.bounds; - let neighbors = boxcastResult.tempHashSet; + // 移动所有的非触发碰撞器并获得最近的碰撞 + let colliders: Collider[] = this.entity.getComponents(Collider); + for (let i = 0; i < colliders.length; i ++){ + let collider = colliders[i]; - for (let j = 0; j < neighbors.length; j ++){ - let neighbor = neighbors[j]; - // 不检测触发器 - if (neighbor.isTrigger) + // 不检测触发器 在我们移动后会重新访问它 + if (collider.isTrigger) continue; - let _internalcollisionResult = collider.collidesWith(neighbor, motion); - if (_internalcollisionResult){ - // 如果碰撞 则退回之前的移动量 - motion = Vector2.subtract(motion, _internalcollisionResult.minimumTranslationVector); + // 获取我们在新位置可能发生碰撞的任何东西 + let bounds = collider.bounds; + bounds.x += motion.x; + bounds.y += motion.y; + let boxcastResult = Physics.boxcastBroadphaseExcludingSelf(collider, bounds, collider.collidesWithLayers); + bounds = boxcastResult.bounds; + let neighbors = boxcastResult.tempHashSet; - // 如果我们碰到多个对象,为了简单起见,只取第一个。 - if (_internalcollisionResult.collider){ - collisionResult = _internalcollisionResult; + for (let j = 0; j < neighbors.length; j ++){ + let neighbor = neighbors[j]; + // 不检测触发器 + if (neighbor.isTrigger) + continue; + + let _internalcollisionResult = collider.collidesWith(neighbor, motion); + if (_internalcollisionResult){ + // 如果碰撞 则退回之前的移动量 + motion = Vector2.subtract(motion, _internalcollisionResult.minimumTranslationVector); + + // 如果我们碰到多个对象,为了简单起见,只取第一个。 + if (_internalcollisionResult.collider){ + collisionResult = _internalcollisionResult; + } } } } + + ListPool.free(colliders); + + return {collisionResult: collisionResult, motion: motion}; } - ListPool.free(colliders); + /** + * 将calculatemomovement应用到实体并更新triggerHelper + * @param motion + */ + public applyMovement(motion: Vector2){ + // 移动实体到它的新位置,如果我们有一个碰撞,否则移动全部数量。当碰撞发生时,运动被更新 + this.entity.position = Vector2.add(this.entity.position, motion); - return {collisionResult: collisionResult, motion: motion}; + // 对所有是触发器的碰撞器与所有宽相位碰撞器进行重叠检查。任何重叠都会导致触发事件。 + if (this._triggerHelper) + this._triggerHelper.update(); + } + + /** + * 通过调用calculateMovement和applyMovement来移动考虑碰撞的实体; + * @param motion + */ + public move(motion: Vector2){ + let movementResult = this.calculateMovement(motion); + let collisionResult = movementResult.collisionResult; + motion = movementResult.motion; + + this.applyMovement(motion); + + return collisionResult; + } } - - /** - * 将calculatemomovement应用到实体并更新triggerHelper - * @param motion - */ - public applyMovement(motion: Vector2){ - // 移动实体到它的新位置,如果我们有一个碰撞,否则移动全部数量。当碰撞发生时,运动被更新 - this.entity.position = Vector2.add(this.entity.position, motion); - - // 对所有是触发器的碰撞器与所有宽相位碰撞器进行重叠检查。任何重叠都会导致触发事件。 - if (this._triggerHelper) - this._triggerHelper.update(); - } - - /** - * 通过调用calculateMovement和applyMovement来移动考虑碰撞的实体; - * @param motion - */ - public move(motion: Vector2){ - let movementResult = this.calculateMovement(motion); - let collisionResult = movementResult.collisionResult; - motion = movementResult.motion; - - this.applyMovement(motion); - - return collisionResult; - } -} \ No newline at end of file +} diff --git a/source/src/ECS/Components/Physics/ProjectileMover.ts b/source/src/ECS/Components/Physics/ProjectileMover.ts index 3d5b8f7b..72e10eec 100644 --- a/source/src/ECS/Components/Physics/ProjectileMover.ts +++ b/source/src/ECS/Components/Physics/ProjectileMover.ts @@ -1,56 +1,58 @@ -/** - * 只向itriggerlistener报告冲突的移动器 - * 该对象将始终移动完整的距离 - */ -class ProjectileMover extends Component { - private _tempTriggerList: ITriggerListener[] = []; - private _collider: Collider; - - public onAddedToEntity(){ - this._collider = this.entity.getComponent(Collider); - if (!this._collider) - console.warn("ProjectileMover has no Collider. ProjectilMover requires a Collider!"); - } - +module es { /** - * 移动考虑碰撞的实体 - * @param motion + * 只向itriggerlistener报告冲突的移动器 + * 该对象将始终移动完整的距离 */ - public move(motion: Vector2): boolean{ - if (!this._collider) - return false; + export class ProjectileMover extends Component { + private _tempTriggerList: ITriggerListener[] = []; + private _collider: Collider; - let didCollide = false; + public onAddedToEntity(){ + this._collider = this.entity.getComponent(Collider); + if (!this._collider) + console.warn("ProjectileMover has no Collider. ProjectilMover requires a Collider!"); + } - // 获取我们在新位置可能发生碰撞的任何东西 - this.entity.position = Vector2.add(this.entity.position, motion); + /** + * 移动考虑碰撞的实体 + * @param motion + */ + public move(motion: Vector2): boolean{ + if (!this._collider) + return false; - // 获取任何可能在新位置发生碰撞的东西 - let neighbors = Physics.boxcastBroadphase(this._collider.bounds, this._collider.collidesWithLayers); - for (let i = 0; i < neighbors.colliders.length; i ++){ - let neighbor = neighbors.colliders[i]; - if (this._collider.overlaps(neighbor) && neighbor.enabled){ - didCollide = true; - this.notifyTriggerListeners(this._collider, neighbor); + let didCollide = false; + + // 获取我们在新位置可能发生碰撞的任何东西 + this.entity.position = Vector2.add(this.entity.position, motion); + + // 获取任何可能在新位置发生碰撞的东西 + let neighbors = Physics.boxcastBroadphase(this._collider.bounds, this._collider.collidesWithLayers); + for (let i = 0; i < neighbors.colliders.length; i ++){ + let neighbor = neighbors.colliders[i]; + if (this._collider.overlaps(neighbor) && neighbor.enabled){ + didCollide = true; + this.notifyTriggerListeners(this._collider, neighbor); + } } + + return didCollide; } - return didCollide; + private notifyTriggerListeners(self: Collider, other: Collider){ + // 通知我们重叠的碰撞器实体上的任何侦听器 + other.entity.getComponents("ITriggerListener", this._tempTriggerList); + for (let i = 0; i < this._tempTriggerList.length; i ++){ + this._tempTriggerList[i].onTriggerEnter(self, other); + } + this._tempTriggerList.length = 0; + + // 通知此实体上的任何侦听器 + this.entity.getComponents("ITriggerListener", this._tempTriggerList); + for (let i = 0; i < this._tempTriggerList.length; i ++){ + this._tempTriggerList[i].onTriggerEnter(other, self); + } + this._tempTriggerList.length = 0; + } } - - private notifyTriggerListeners(self: Collider, other: Collider){ - // 通知我们重叠的碰撞器实体上的任何侦听器 - other.entity.getComponents("ITriggerListener", this._tempTriggerList); - for (let i = 0; i < this._tempTriggerList.length; i ++){ - this._tempTriggerList[i].onTriggerEnter(self, other); - } - this._tempTriggerList.length = 0; - - // 通知此实体上的任何侦听器 - this.entity.getComponents("ITriggerListener", this._tempTriggerList); - for (let i = 0; i < this._tempTriggerList.length; i ++){ - this._tempTriggerList[i].onTriggerEnter(other, self); - } - this._tempTriggerList.length = 0; - } -} \ No newline at end of file +} diff --git a/source/src/ECS/Components/ScrollingSpriteRenderer.ts b/source/src/ECS/Components/ScrollingSpriteRenderer.ts index f2e1ba30..9b8e38d2 100644 --- a/source/src/ECS/Components/ScrollingSpriteRenderer.ts +++ b/source/src/ECS/Components/ScrollingSpriteRenderer.ts @@ -1,37 +1,37 @@ /// -class ScrollingSpriteRenderer extends TiledSpriteRenderer { - public scrollSpeedX = 15; - public scroolSpeedY = 0; - private _scrollX = 0; - private _scrollY = 0; +module es { + export class ScrollingSpriteRenderer extends TiledSpriteRenderer { + public scrollSpeedX = 15; + public scroolSpeedY = 0; + private _scrollX = 0; + private _scrollY = 0; - public update(){ - this._scrollX += this.scrollSpeedX * Time.deltaTime; - this._scrollY += this.scroolSpeedY * Time.deltaTime; - this.sourceRect.x = this._scrollX; - this.sourceRect.y = this._scrollY; + public update(){ + this._scrollX += this.scrollSpeedX * Time.deltaTime; + this._scrollY += this.scroolSpeedY * Time.deltaTime; + this.sourceRect.x = this._scrollX; + this.sourceRect.y = this._scrollY; + } + + public render(camera: Camera) { + if (!this.sprite) + return; + + super.render(camera); + + let renderTexture = new egret.RenderTexture(); + let cacheBitmap = new egret.DisplayObjectContainer(); + cacheBitmap.removeChildren(); + cacheBitmap.addChild(this.leftTexture); + cacheBitmap.addChild(this.rightTexture); + + this.leftTexture.x = this.sourceRect.x; + this.rightTexture.x = this.sourceRect.x - this.sourceRect.width; + this.leftTexture.y = this.sourceRect.y; + this.rightTexture.y = this.sourceRect.y; + + cacheBitmap.cacheAsBitmap = true; + renderTexture.drawToTexture(cacheBitmap, new egret.Rectangle(0, 0, this.sourceRect.width, this.sourceRect.height)); + } } - - public render(camera: Camera) { - if (!this.sprite) - return; - - super.render(camera); - - let renderTexture = new egret.RenderTexture(); - let cacheBitmap = new egret.DisplayObjectContainer(); - cacheBitmap.removeChildren(); - cacheBitmap.addChild(this.leftTexture); - cacheBitmap.addChild(this.rightTexture); - - this.leftTexture.x = this.sourceRect.x; - this.rightTexture.x = this.sourceRect.x - this.sourceRect.width; - this.leftTexture.y = this.sourceRect.y; - this.rightTexture.y = this.sourceRect.y; - - cacheBitmap.cacheAsBitmap = true; - renderTexture.drawToTexture(cacheBitmap, new egret.Rectangle(0, 0, this.sourceRect.width, this.sourceRect.height)); - - this.bitmap.texture = renderTexture; - } -} \ No newline at end of file +} diff --git a/source/src/ECS/Components/Sprite.ts b/source/src/ECS/Components/Sprite.ts index ba8ced5e..19c54fb4 100644 --- a/source/src/ECS/Components/Sprite.ts +++ b/source/src/ECS/Components/Sprite.ts @@ -1,24 +1,26 @@ -class Sprite { - public texture2D: egret.Texture; - public readonly sourceRect: Rectangle; - public readonly center: Vector2; - public origin: Vector2; - public readonly uvs: Rectangle = new Rectangle(); +module es { + export class Sprite { + public texture2D: egret.Texture; + public readonly sourceRect: Rectangle; + public readonly center: Vector2; + public origin: Vector2; + public readonly uvs: Rectangle = new Rectangle(); - constructor(texture: egret.Texture, - sourceRect: Rectangle = new Rectangle(0, 0, texture.textureWidth, texture.textureHeight), - origin: Vector2 = sourceRect.getHalfSize()) { - this.texture2D = texture; - this.sourceRect = sourceRect; - this.center = new Vector2(sourceRect.width * 0.5, sourceRect.height * 0.5); - this.origin = origin; + constructor(texture: egret.Texture, + sourceRect: Rectangle = new Rectangle(0, 0, texture.textureWidth, texture.textureHeight), + origin: Vector2 = sourceRect.getHalfSize()) { + this.texture2D = texture; + this.sourceRect = sourceRect; + this.center = new Vector2(sourceRect.width * 0.5, sourceRect.height * 0.5); + this.origin = origin; - let inverseTexW = 1 / texture.textureWidth; - let inverseTexH = 1 / texture.textureHeight; + let inverseTexW = 1 / texture.textureWidth; + let inverseTexH = 1 / texture.textureHeight; - this.uvs.x = sourceRect.x * inverseTexW; - this.uvs.y = sourceRect.y * inverseTexH; - this.uvs.width = sourceRect.width * inverseTexW; - this.uvs.height = sourceRect.height * inverseTexH; + this.uvs.x = sourceRect.x * inverseTexW; + this.uvs.y = sourceRect.y * inverseTexH; + this.uvs.width = sourceRect.width * inverseTexW; + this.uvs.height = sourceRect.height * inverseTexH; + } } -} \ No newline at end of file +} diff --git a/source/src/ECS/Components/SpriteAnimation.ts b/source/src/ECS/Components/SpriteAnimation.ts index 15ee5ea3..77e33923 100644 --- a/source/src/ECS/Components/SpriteAnimation.ts +++ b/source/src/ECS/Components/SpriteAnimation.ts @@ -1,9 +1,11 @@ -class SpriteAnimation { - public readonly sprites: Sprite[]; - public readonly frameRate: number; +module es { + export class SpriteAnimation { + public readonly sprites: Sprite[]; + public readonly frameRate: number; - constructor(sprites: Sprite[], frameRate: number){ - this.sprites = sprites; - this.frameRate = frameRate; + constructor(sprites: Sprite[], frameRate: number){ + this.sprites = sprites; + this.frameRate = frameRate; + } } -} \ No newline at end of file +} diff --git a/source/src/ECS/Components/SpriteAnimator.ts b/source/src/ECS/Components/SpriteAnimator.ts index dc3ee505..f4b6af29 100644 --- a/source/src/ECS/Components/SpriteAnimator.ts +++ b/source/src/ECS/Components/SpriteAnimator.ts @@ -1,108 +1,84 @@ /// -class SpriteAnimator extends SpriteRenderer { - /** 在动画完成时触发,包括动画名称; */ - public onAnimationCompletedEvent: Function; - /** 动画播放速度 */ - public speed = 1; - /** 动画的当前状态 */ - public animationState = State.none; - /** 当前动画 */ - public currentAnimation: SpriteAnimation; - /** 当前动画的名称 */ - public currentAnimationName: string; - /** 当前动画的精灵数组中当前帧的索引 */ - public currentFrame: number; - /** 检查当前动画是否正在运行 */ - public get isRunning(): boolean{ - return this.animationState == State.running; +module es { + export enum LoopMode { + /** 在一个循环序列[A][B][C][A][B][C][A][B][C]... */ + loop, + /** [A][B][C]然后暂停,设置时间为0 [A] */ + once, + /** [A][B][C]。当它到达终点时,它会继续播放最后一帧,并且不会停止播放 */ + clampForever, + /** 以一个乒乓循环的方式永远播放这个序列 [A][B][C][B][A][B][C][B]... */ + pingPong, + /** 将顺序向前播放一次,然后返回到开始[A][B][C][B][A],然后暂停并设置时间为0 */ + pingPongOnce, } - /** 提供对可用动画列表的访问 */ - public get animations(){ - return this._animations; - } - private _animations: Map = new Map(); - private _elapsedTime: number = 0; - private _loopMode: LoopMode; - - constructor(sprite?: Sprite){ - super(); - - if (sprite) this.setSprite(sprite); + export enum State { + none, + running, + paused, + completed, } - /** - * 添加一个SpriteAnimation - * @param name - * @param animation - */ - public addAnimation(name: string, animation: SpriteAnimation): SpriteAnimator{ - if (!this.sprite && animation.sprites.length > 0) - this.setSprite(animation.sprites[0]); - this._animations[name] = animation; - return this; - } + export class SpriteAnimator extends SpriteRenderer { + /** + * 在动画完成时触发,包括动画名称 + */ + public onAnimationCompletedEvent: (string) => {}; + /** + * 动画播放速度 + */ + public speed = 1; + /** + * 动画的当前状态 + */ + public animationState = State.none; + /** + * 当前动画 + */ + public currentAnimation: SpriteAnimation; + /** + * 当前动画的名称 + */ + public currentAnimationName: string; + /** + * 当前动画的精灵数组中当前帧的索引 + */ + public currentFrame: number; - /** - * 以给定的名称放置动画。如果没有指定循环模式,则默认为循环 - * @param name - * @param loopMode - */ - public play(name: string, loopMode: LoopMode = null){ - this.currentAnimation = this._animations[name]; - this.currentAnimationName = name; - this.currentFrame = 0; - this.animationState = State.running; + /** + * 检查当前动画是否正在运行 + */ + public get isRunning(): boolean { + return this.animationState == State.running; + } - this.sprite = this.currentAnimation.sprites[0]; - this._elapsedTime = 0; - this._loopMode = loopMode ? loopMode : LoopMode.loop; - } + /** 提供对可用动画列表的访问 */ + public get animations() { + return this._animations; + } - /** - * 检查动画是否正在播放(即动画是活动的)。它可能仍然处于暂停状态) - * @param name - */ - public isAnimationActive(name: string): boolean{ - return this.currentAnimation && this.currentAnimationName == name; - } + private _animations: Map = new Map(); + public _elapsedTime: number = 0; + public _loopMode: LoopMode; - /** - * 暂停动画 - */ - public pause(){ - this.animationState = State.paused; - } + constructor(sprite?: Sprite) { + super(sprite); + } - /** - * 继续动画 - */ - public unPause(){ - this.animationState = State.running; - } + public update() { + if (this.animationState != State.running || !this.currentAnimation) return; - /** - * 停止当前动画并将其设为null - */ - public stop(){ - this.currentAnimation = null; - this.currentAnimationName = null; - this.currentFrame = 0; - this.animationState = State.none; - } + let animation = this.currentAnimation; + let secondsPerFrame = 1 / (animation.frameRate * this.speed); + let iterationDuration = secondsPerFrame * animation.sprites.length; - public update(){ - if (this.animationState != State.running || !this.currentAnimation) return; + this._elapsedTime += Time.deltaTime; + let time = Math.abs(this._elapsedTime); - let animation = this.currentAnimation; - let secondsPerFrame = 1 / (animation.frameRate * this.speed); - let iterationDuration = secondsPerFrame * animation.sprites.length; - - this._elapsedTime += Time.deltaTime; - let time = Math.abs(this._elapsedTime); - - if (this._loopMode == LoopMode.once && time > iterationDuration || - this._loopMode == LoopMode.pingPongOnce && time > iterationDuration * 2){ + // Once和PingPongOnce完成后重置为Time = 0 + if (this._loopMode == LoopMode.once && time > iterationDuration || + this._loopMode == LoopMode.pingPongOnce && time > iterationDuration * 2) { this.animationState = State.completed; this._elapsedTime = 0; this.currentFrame = 0; @@ -113,34 +89,76 @@ class SpriteAnimator extends SpriteRenderer { // 弄清楚我们在哪个坐标系上 let i = Math.floor(time / secondsPerFrame); let n = animation.sprites.length; - if (n > 2 && (this._loopMode == LoopMode.pingPong || this._loopMode == LoopMode.pingPongOnce)){ + if (n > 2 && (this._loopMode == LoopMode.pingPong || this._loopMode == LoopMode.pingPongOnce)) { // pingpong let maxIndex = n - 1; this.currentFrame = maxIndex - Math.abs(maxIndex - i % (maxIndex * 2)); - }else{ + } else { this.currentFrame = i % n; } this.sprite = animation.sprites[this.currentFrame]; + } + + /** + * 添加一个SpriteAnimation + * @param name + * @param animation + */ + public addAnimation(name: string, animation: SpriteAnimation): SpriteAnimator { + // 如果我们没有精灵,使用我们找到的第一帧 + if (!this.sprite && animation.sprites.length > 0) + this.setSprite(animation.sprites[0]); + this._animations[name] = animation; + return this; + } + + /** + * 以给定的名称放置动画。如果没有指定循环模式,则默认为循环 + * @param name + * @param loopMode + */ + public play(name: string, loopMode: LoopMode = null) { + this.currentAnimation = this._animations[name]; + this.currentAnimationName = name; + this.currentFrame = 0; + this.animationState = State.running; + + this.sprite = this.currentAnimation.sprites[0]; + this._elapsedTime = 0; + this._loopMode = loopMode ? loopMode : LoopMode.loop; + } + + /** + * 检查动画是否正在播放(即动画是活动的)。它可能仍然处于暂停状态) + * @param name + */ + public isAnimationActive(name: string): boolean { + return this.currentAnimation && this.currentAnimationName == name; + } + + /** + * 暂停动画 + */ + public pause() { + this.animationState = State.paused; + } + + /** + * 继续动画 + */ + public unPause() { + this.animationState = State.running; + } + + /** + * 停止当前动画并将其设为null + */ + public stop() { + this.currentAnimation = null; + this.currentAnimationName = null; + this.currentFrame = 0; + this.animationState = State.none; + } } } - -enum LoopMode { - /** 在一个循环序列[A][B][C][A][B][C][A][B][C]... */ - loop, - /** [A][B][C]然后暂停,设置时间为0 [A] */ - once, - /** [A][B][C]。当它到达终点时,它会继续播放最后一帧,并且不会停止播放 */ - clampForever, - /** 以一个乒乓循环的方式永远播放这个序列 [A][B][C][B][A][B][C][B]... */ - pingPong, - /** 将顺序向前播放一次,然后返回到开始[A][B][C][B][A],然后暂停并设置时间为0 */ - pingPongOnce, -} - -enum State { - none, - running, - paused, - completed, -} \ No newline at end of file diff --git a/source/src/ECS/Components/SpriteRenderer.ts b/source/src/ECS/Components/SpriteRenderer.ts index 6db35138..f57f66e8 100644 --- a/source/src/ECS/Components/SpriteRenderer.ts +++ b/source/src/ECS/Components/SpriteRenderer.ts @@ -1,8 +1,8 @@ module es { export class SpriteRenderer extends RenderableComponent { - public get bounds(){ - if (this._areBoundsDirty){ - if (this._sprite){ + public get bounds() { + if (this._areBoundsDirty) { + if (this._sprite) { this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, this._origin, this.entity.transform.scale, this.entity.transform.rotation, this._sprite.sourceRect.width, this._sprite.sourceRect.height); @@ -16,7 +16,7 @@ module es { /** * 精灵的原点。这是在设置精灵时自动设置的 */ - public get origin(): Vector2{ + public get origin(): Vector2 { return this._origin; } @@ -24,7 +24,7 @@ module es { * 精灵的原点。这是在设置精灵时自动设置的 * @param value */ - public set origin(value: Vector2){ + public set origin(value: Vector2) { this.setOrigin(value); } @@ -32,7 +32,7 @@ module es { * 用归一化方法设置原点 * x/y 均为 0-1 */ - public get originNormalized(): Vector2{ + public get originNormalized(): Vector2 { return new Vector2(this._origin.x / this.width * this.entity.transform.scale.x, this._origin.y / this.height * this.entity.transform.scale.y); } @@ -42,7 +42,7 @@ module es { * x/y 均为 0-1 * @param value */ - public set originNormalized(value: Vector2){ + public set originNormalized(value: Vector2) { this.setOrigin(new Vector2(value.x * this.width / this.entity.transform.scale.x, value.y * this.height / this.entity.transform.scale.y)); } @@ -67,11 +67,11 @@ module es { protected _origin: Vector2; protected _sprite: Sprite; - constructor(sprite: Sprite | egret.Texture) { + constructor(sprite: Sprite | egret.Texture = null) { super(); if (sprite instanceof Sprite) this.setSprite(sprite); - else if(sprite instanceof egret.Texture) + else if (sprite instanceof egret.Texture) this.setSprite(new Sprite(sprite)); } @@ -92,8 +92,8 @@ module es { * 设置可渲染的原点 * @param origin */ - public setOrigin(origin: Vector2): SpriteRenderer{ - if (this._origin != origin){ + public setOrigin(origin: Vector2): SpriteRenderer { + if (this._origin != origin) { this._origin = origin; this._areBoundsDirty = true; } @@ -106,7 +106,7 @@ module es { * x/y 均为 0-1 * @param value */ - public setOriginNormalized(value: Vector2): SpriteRenderer{ + public setOriginNormalized(value: Vector2): SpriteRenderer { this.setOrigin(new Vector2(value.x * this.width / this.entity.transform.scale.x, value.y * this.height / this.entity.transform.scale.y)); return this; diff --git a/source/src/ECS/Components/TiledSpriteRenderer.ts b/source/src/ECS/Components/TiledSpriteRenderer.ts index e5b097f4..a9316e09 100644 --- a/source/src/ECS/Components/TiledSpriteRenderer.ts +++ b/source/src/ECS/Components/TiledSpriteRenderer.ts @@ -1,57 +1,57 @@ /// -/** - * 滚动由两张图片组合而成 - */ -class TiledSpriteRenderer extends SpriteRenderer { - protected sourceRect: Rectangle; - protected leftTexture: egret.Bitmap; - protected rightTexture: egret.Bitmap; +module es { + /** + * 滚动由两张图片组合而成 + */ + export class TiledSpriteRenderer extends SpriteRenderer { + protected sourceRect: Rectangle; + protected leftTexture: egret.Bitmap; + protected rightTexture: egret.Bitmap; - public get scrollX() { - return this.sourceRect.x; + public get scrollX() { + return this.sourceRect.x; + } + public set scrollX(value: number) { + this.sourceRect.x = value; + } + public get scrollY() { + return this.sourceRect.y; + } + public set scrollY(value: number) { + this.sourceRect.y = value; + } + + constructor(sprite: Sprite) { + super(sprite); + + this.leftTexture = new egret.Bitmap(); + this.rightTexture = new egret.Bitmap(); + this.leftTexture.texture = sprite.texture2D; + this.rightTexture.texture = sprite.texture2D; + + this.setSprite(sprite); + this.sourceRect = sprite.sourceRect; + } + + public render(camera: es.Camera) { + if (!this.sprite) + return; + + super.render(camera); + + let renderTexture = new egret.RenderTexture(); + let cacheBitmap = new egret.DisplayObjectContainer(); + cacheBitmap.removeChildren(); + cacheBitmap.addChild(this.leftTexture); + cacheBitmap.addChild(this.rightTexture); + + this.leftTexture.x = this.sourceRect.x; + this.rightTexture.x = this.sourceRect.x - this.sourceRect.width; + this.leftTexture.y = this.sourceRect.y; + this.rightTexture.y = this.sourceRect.y; + + cacheBitmap.cacheAsBitmap = true; + renderTexture.drawToTexture(cacheBitmap, new egret.Rectangle(0, 0, this.sourceRect.width, this.sourceRect.height)); + } } - public set scrollX(value: number) { - this.sourceRect.x = value; - } - public get scrollY() { - return this.sourceRect.y; - } - public set scrollY(value: number) { - this.sourceRect.y = value; - } - - constructor(sprite: Sprite) { - super(); - - this.leftTexture = new egret.Bitmap(); - this.rightTexture = new egret.Bitmap(); - this.leftTexture.texture = sprite.texture2D; - this.rightTexture.texture = sprite.texture2D; - - this.setSprite(sprite); - this.sourceRect = sprite.sourceRect; - } - - public render(camera: Camera) { - if (!this.sprite) - return; - - super.render(camera); - - let renderTexture = new egret.RenderTexture(); - let cacheBitmap = new egret.DisplayObjectContainer(); - cacheBitmap.removeChildren(); - cacheBitmap.addChild(this.leftTexture); - cacheBitmap.addChild(this.rightTexture); - - this.leftTexture.x = this.sourceRect.x; - this.rightTexture.x = this.sourceRect.x - this.sourceRect.width; - this.leftTexture.y = this.sourceRect.y; - this.rightTexture.y = this.sourceRect.y; - - cacheBitmap.cacheAsBitmap = true; - renderTexture.drawToTexture(cacheBitmap, new egret.Rectangle(0, 0, this.sourceRect.width, this.sourceRect.height)); - - this.bitmap.texture = renderTexture; - } -} \ No newline at end of file +} diff --git a/source/src/ECS/Utils/BitSet.ts b/source/src/ECS/Utils/BitSet.ts index d75c2f35..7dcd6544 100644 --- a/source/src/ECS/Utils/BitSet.ts +++ b/source/src/ECS/Utils/BitSet.ts @@ -1,133 +1,135 @@ -/** - * 这个类可以从两方面来考虑。你可以把它看成一个位向量或者一组非负整数。这个名字有点误导人。 - * - * 它是由一个位向量实现的,但同样可以把它看成是一个非负整数的集合;集合中的每个整数由对应索引处的集合位表示。该结构的大小由集合中的最大整数决定。 - */ -class BitSet{ - private static LONG_MASK: number = 0x3f; - private _bits: number[]; +module es { + /** + * 这个类可以从两方面来考虑。你可以把它看成一个位向量或者一组非负整数。这个名字有点误导人。 + * + * 它是由一个位向量实现的,但同样可以把它看成是一个非负整数的集合;集合中的每个整数由对应索引处的集合位表示。该结构的大小由集合中的最大整数决定。 + */ + export class BitSet{ + private static LONG_MASK: number = 0x3f; + private _bits: number[]; - constructor(nbits: number = 64){ - let length = nbits >> 6; - if ((nbits & BitSet.LONG_MASK) != 0) - length ++; + constructor(nbits: number = 64){ + let length = nbits >> 6; + if ((nbits & BitSet.LONG_MASK) != 0) + length ++; - this._bits = new Array(length); - } + this._bits = new Array(length); + } - public and(bs: BitSet){ - let max = Math.min(this._bits.length, bs._bits.length); - let i; - for (let i = 0; i < max; ++i) - this._bits[i] &= bs._bits[i]; + public and(bs: BitSet){ + let max = Math.min(this._bits.length, bs._bits.length); + let i; + for (let i = 0; i < max; ++i) + this._bits[i] &= bs._bits[i]; - while (i < this._bits.length) - this._bits[i ++] = 0; - } + while (i < this._bits.length) + this._bits[i ++] = 0; + } - public andNot(bs: BitSet){ - let i = Math.min(this._bits.length, bs._bits.length); - while(--i >= 0) - this._bits[i] &= ~bs._bits[i]; - } + public andNot(bs: BitSet){ + let i = Math.min(this._bits.length, bs._bits.length); + while(--i >= 0) + this._bits[i] &= ~bs._bits[i]; + } - public cardinality(): number{ - let card = 0; - for (let i = this._bits.length - 1; i >= 0; i --){ - let a = this._bits[i]; + public cardinality(): number{ + let card = 0; + for (let i = this._bits.length - 1; i >= 0; i --){ + let a = this._bits[i]; - if (a == 0) - continue; + if (a == 0) + continue; - if (a == -1){ - card += 64; - continue; + if (a == -1){ + card += 64; + continue; + } + + a = ((a >> 1) & 0x5555555555555555) + (a & 0x5555555555555555); + a = ((a >> 2) & 0x3333333333333333) + (a & 0x3333333333333333); + let b = ((a >> 32) + a); + b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f); + b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff); + card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff); } - a = ((a >> 1) & 0x5555555555555555) + (a & 0x5555555555555555); - a = ((a >> 2) & 0x3333333333333333) + (a & 0x3333333333333333); - let b = ((a >> 32) + a); - b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f); - b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff); - card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff); + return card; } - return card; - } + public clear(pos?: number){ + if (pos != undefined){ + let offset = pos >> 6; + this.ensure(offset); + this._bits[offset] &= ~(1 << pos); + }else{ + for (let i = 0; i < this._bits.length; i ++) + this._bits[i] = 0; + } + } - public clear(pos?: number){ - if (pos != undefined){ + private ensure(lastElt: number){ + if (lastElt >= this._bits.length){ + let nd = new Number[lastElt + 1]; + nd = this._bits.copyWithin(0, 0, this._bits.length); + this._bits = nd; + } + } + + public get(pos: number): boolean{ let offset = pos >> 6; - this.ensure(offset); - this._bits[offset] &= ~(1 << pos); - }else{ - for (let i = 0; i < this._bits.length; i ++) - this._bits[i] = 0; - } - } - - private ensure(lastElt: number){ - if (lastElt >= this._bits.length){ - let nd = new Number[lastElt + 1]; - nd = this._bits.copyWithin(0, 0, this._bits.length); - this._bits = nd; - } - } - - public get(pos: number): boolean{ - let offset = pos >> 6; - if (offset >= this._bits.length) - return false; - - return (this._bits[offset] & (1 << pos)) != 0; - } - - public intersects(set: BitSet){ - let i = Math.min(this._bits.length, set._bits.length); - while (--i >= 0){ - if ((this._bits[i] & set._bits[i]) != 0) - return true; - } - - return false; - } - - public isEmpty(): boolean{ - for (let i = this._bits.length - 1; i >= 0; i --){ - if (this._bits[i]) + if (offset >= this._bits.length) return false; + + return (this._bits[offset] & (1 << pos)) != 0; } - return true; - } + public intersects(set: BitSet){ + let i = Math.min(this._bits.length, set._bits.length); + while (--i >= 0){ + if ((this._bits[i] & set._bits[i]) != 0) + return true; + } - public nextSetBit(from: number){ - let offset = from >> 6; - let mask = 1 << from; - while (offset < this._bits.length){ - let h = this._bits[offset]; - do { - if ((h & mask) != 0) - return from; - - mask <<= 1; - from ++; - } while (mask != 0); - - mask = 1; - offset ++; + return false; } - return -1; - } + public isEmpty(): boolean{ + for (let i = this._bits.length - 1; i >= 0; i --){ + if (this._bits[i]) + return false; + } - public set(pos: number, value: boolean = true){ - if (value){ - let offset = pos >> 6; - this.ensure(offset); - this._bits[offset] |= 1 << pos; - }else{ - this.clear(pos); + return true; + } + + public nextSetBit(from: number){ + let offset = from >> 6; + let mask = 1 << from; + while (offset < this._bits.length){ + let h = this._bits[offset]; + do { + if ((h & mask) != 0) + return from; + + mask <<= 1; + from ++; + } while (mask != 0); + + mask = 1; + offset ++; + } + + return -1; + } + + public set(pos: number, value: boolean = true){ + if (value){ + let offset = pos >> 6; + this.ensure(offset); + this._bits[offset] |= 1 << pos; + }else{ + this.clear(pos); + } } } -} \ No newline at end of file +} diff --git a/source/src/ECS/Utils/ComponentTypeManager.ts b/source/src/ECS/Utils/ComponentTypeManager.ts index f8ca330c..7dc20b4b 100644 --- a/source/src/ECS/Utils/ComponentTypeManager.ts +++ b/source/src/ECS/Utils/ComponentTypeManager.ts @@ -1,18 +1,20 @@ -class ComponentTypeManager{ - private static _componentTypesMask: Map = new Map(); +module es { + export class ComponentTypeManager{ + private static _componentTypesMask: Map = new Map(); - public static add(type){ - if (!this._componentTypesMask.has(type)) - this._componentTypesMask[type] = this._componentTypesMask.size; - } - - public static getIndexFor(type){ - let v = -1; - if (!this._componentTypesMask.has(type)){ - this.add(type); - v = this._componentTypesMask.get(type); + public static add(type){ + if (!this._componentTypesMask.has(type)) + this._componentTypesMask[type] = this._componentTypesMask.size; } - return v; + public static getIndexFor(type){ + let v = -1; + if (!this._componentTypesMask.has(type)){ + this.add(type); + v = this._componentTypesMask.get(type); + } + + return v; + } } -} \ No newline at end of file +} diff --git a/source/src/ECS/Utils/EntityProcessorList.ts b/source/src/ECS/Utils/EntityProcessorList.ts index eedd5fdd..148f08b7 100644 --- a/source/src/ECS/Utils/EntityProcessorList.ts +++ b/source/src/ECS/Utils/EntityProcessorList.ts @@ -1,69 +1,71 @@ -class EntityProcessorList { - private _processors: EntitySystem[] = []; +module es { + export class EntityProcessorList { + private _processors: EntitySystem[] = []; - public add(processor: EntitySystem){ - this._processors.push(processor); - } - - public remove(processor: EntitySystem){ - this._processors.remove(processor); - } - - public onComponentAdded(entity: Entity){ - this.notifyEntityChanged(entity); - } - - public onComponentRemoved(entity: Entity){ - this.notifyEntityChanged(entity); - } - - public onEntityAdded(entity: Entity){ - this.notifyEntityChanged(entity); - } - - public onEntityRemoved(entity: Entity){ - this.removeFromProcessors(entity); - } - - protected notifyEntityChanged(entity: Entity){ - for (let i = 0; i < this._processors.length; i ++){ - this._processors[i].onChanged(entity); - } - } - - protected removeFromProcessors(entity: Entity){ - for (let i = 0; i < this._processors.length; i ++){ - this._processors[i].remove(entity); - } - } - - public begin(){ - - } - - public update(){ - for (let i = 0; i < this._processors.length; i++){ - this._processors[i].update(); - } - } - - public lateUpdate(){ - for (let i = 0; i < this._processors.length; i ++){ - this._processors[i].lateUpdate(); - } - } - - public end(){ - - } - - public getProcessor(): T{ - for (let i = 0; i < this._processors.length; i ++){ - let processor = this._processors[i]; - if (processor instanceof EntitySystem) - return processor as T; + public add(processor: EntitySystem){ + this._processors.push(processor); } - return null; + public remove(processor: EntitySystem){ + this._processors.remove(processor); + } + + public onComponentAdded(entity: Entity){ + this.notifyEntityChanged(entity); + } + + public onComponentRemoved(entity: Entity){ + this.notifyEntityChanged(entity); + } + + public onEntityAdded(entity: Entity){ + this.notifyEntityChanged(entity); + } + + public onEntityRemoved(entity: Entity){ + this.removeFromProcessors(entity); + } + + protected notifyEntityChanged(entity: Entity){ + for (let i = 0; i < this._processors.length; i ++){ + this._processors[i].onChanged(entity); + } + } + + protected removeFromProcessors(entity: Entity){ + for (let i = 0; i < this._processors.length; i ++){ + this._processors[i].remove(entity); + } + } + + public begin(){ + + } + + public update(){ + for (let i = 0; i < this._processors.length; i++){ + this._processors[i].update(); + } + } + + public lateUpdate(){ + for (let i = 0; i < this._processors.length; i ++){ + this._processors[i].lateUpdate(); + } + } + + public end(){ + + } + + public getProcessor(): T{ + for (let i = 0; i < this._processors.length; i ++){ + let processor = this._processors[i]; + if (processor instanceof EntitySystem) + return processor as T; + } + + return null; + } } -} \ No newline at end of file +} diff --git a/source/src/Physics/Shapes/Box.ts b/source/src/Physics/Shapes/Box.ts index e9e23c13..27d98e3d 100644 --- a/source/src/Physics/Shapes/Box.ts +++ b/source/src/Physics/Shapes/Box.ts @@ -55,18 +55,20 @@ module es { public overlaps(other: Shape){ // 特殊情况,这一个高性能方式实现,其他情况则使用polygon方法检测 - if (other instanceof Box) - return this.bounds.intersects(other.bounds); + if (this.isUnrotated){ + if (other instanceof Box) + return this.bounds.intersects(other.bounds); - if (other instanceof Circle) - return Collisions.isRectToCircle(this.bounds, other.position, other.radius); + if (other instanceof Circle) + return Collisions.isRectToCircle(this.bounds, other.position, other.radius); + } return super.overlaps(other); } public collidesWithShape(other: Shape){ // 特殊情况,这一个高性能方式实现,其他情况则使用polygon方法检测 - if (other instanceof Box){ + if (other instanceof Box && (other as Box).isUnrotated){ return ShapeCollisions.boxToBox(this, other); } @@ -75,14 +77,11 @@ module es { return super.collidesWithShape(other); } - - - /** - * - * @param point - */ public containsPoint(point: Vector2){ - return this.bounds.contains(point.x, point.y); + if (this.isUnrotated) + return this.bounds.contains(point.x, point.y); + + return super.containsPoint(point); } } } \ No newline at end of file diff --git a/source/src/Physics/Shapes/Circle.ts b/source/src/Physics/Shapes/Circle.ts index 2ad27222..70e19752 100644 --- a/source/src/Physics/Shapes/Circle.ts +++ b/source/src/Physics/Shapes/Circle.ts @@ -1,52 +1,69 @@ /// -class Circle extends Shape { - public radius: number; - public _originalRadius: number; - public center = new Vector2(); - public get position(){ - return new Vector2(this.parent.x, this.parent.y); - } +module es { + export class Circle extends Shape { + public radius: number; + public _originalRadius: number; - public get bounds(){ - return new Rectangle().setEgretRect(this.getBounds()); - } - - constructor(radius: number) { - super(); - this.radius = radius; - this._originalRadius = radius; - } - - public pointCollidesWithShape(point: Vector2): CollisionResult { - return ShapeCollisions.pointToCircle(point, this); - } - - public collidesWithShape(other: Shape): CollisionResult { - if (other instanceof Box) { - return ShapeCollisions.circleToBox(this, other); + constructor(radius: number) { + super(); + this.radius = radius; + this._originalRadius = radius; } - if (other instanceof Circle) { - return ShapeCollisions.circleToCircle(this, other); + public recalculateBounds(collider: es.Collider) { + // 如果我们没有旋转或不关心TRS我们使用localOffset作为中心 + this.center = collider.localOffset; + + if (collider.shouldColliderScaleAndRotateWithTransform) { + // 我们只将直线缩放为一个圆,所以我们将使用最大值 + let scale = collider.entity.transform.scale; + let hasUnitScale = scale.x == 1 && scale.y == 1; + let maxScale = Math.max(scale.x, scale.y); + this.radius = this._originalRadius * maxScale; + + if (collider.entity.transform.rotation != 0) { + // 为了处理偏移原点的旋转,我们只需要将圆心围绕(0,0)在一个圆上移动,我们的偏移量就是0角 + let offsetAngle = Math.atan2(collider.localOffset.y, collider.localOffset.x) * MathHelper.Rad2Deg; + let offsetLength = hasUnitScale ? collider._localOffsetLength : Vector2.multiply(collider.localOffset, collider.entity.transform.scale).length(); + this.center = MathHelper.pointOnCirlce(Vector2.zero, offsetLength, collider.entity.transform.rotation + offsetAngle); + } + } + + this.position = Vector2.add(collider.transform.position, this.center); + this.bounds = new Rectangle(this.position.x - this.radius, this.position.y - this.radius, this.radius * 2, this.radius * 2); } - if (other instanceof Polygon) { - return ShapeCollisions.circleToPolygon(this, other); + public overlaps(other: Shape) { + if (other instanceof Box && (other as Box).isUnrotated) + return Collisions.isRectToCircle(other.bounds, this.position, this.radius); + + if (other instanceof Circle) + return Collisions.isCircleToCircle(this.position, this.radius, other.position, (other as Circle).radius); + + if (other instanceof Polygon) + return ShapeCollisions.circleToPolygon(this, other); + + throw new Error(`overlaps of circle to ${other} are not supported`); } - throw new Error(`Collisions of Circle to ${other} are not supported`); + public collidesWithShape(other: Shape): CollisionResult { + if (other instanceof Box && (other as Box).isUnrotated) { + return ShapeCollisions.circleToBox(this, other); + } + + if (other instanceof Circle) { + return ShapeCollisions.circleToCircle(this, other); + } + + if (other instanceof Polygon) { + return ShapeCollisions.circleToPolygon(this, other); + } + + throw new Error(`Collisions of Circle to ${other} are not supported`); + } + + public pointCollidesWithShape(point: Vector2): CollisionResult { + return ShapeCollisions.pointToCircle(point, this); + } } - - public overlaps(other: Shape){ - if (other instanceof Box) - return Collisions.isRectToCircle(other.bounds, this.position, this.radius); - - if (other instanceof Circle) - return Collisions.isCircleToCircle(this.position, this.radius, other.position, (other as Circle).radius); - - if (other instanceof Polygon) - return ShapeCollisions.circleToPolygon(this, other); - - throw new Error(`overlaps of circle to ${other} are not supported`); - } -} \ No newline at end of file +} diff --git a/source/src/Physics/Verlet/SpatialHash.ts b/source/src/Physics/Verlet/SpatialHash.ts index ca7b10f1..015c5786 100644 --- a/source/src/Physics/Verlet/SpatialHash.ts +++ b/source/src/Physics/Verlet/SpatialHash.ts @@ -1,249 +1,269 @@ -class SpatialHash { - public gridBounds: Rectangle = new Rectangle(); +module es { + export class SpatialHash { + public gridBounds: Rectangle = new Rectangle(); - private _raycastParser: RaycastResultParser; - /** 散列中每个单元格的大小 */ - private _cellSize: number; - /** 1除以单元格大小。缓存结果,因为它被大量使用。 */ - private _inverseCellSize: number; - /** 缓存的循环用于重叠检查 */ - private _overlapTestCircle: Circle = new Circle(0); - /** 用于返回冲突信息的共享HashSet */ - private _tempHashSet: Collider[] = []; - /** 保存所有数据的字典 */ - private _cellDict: NumberDictionary = new NumberDictionary(); + public _raycastParser: RaycastResultParser; + /** + * 散列中每个单元格的大小 + */ + public _cellSize: number; + /** + * 1除以单元格大小。缓存结果,因为它被大量使用。 + */ + public _inverseCellSize: number; + /** + * 缓存的循环用于重叠检查 + */ + public _overlapTestCircle: Circle = new Circle(0); + /** + * 保存所有数据的字典 + */ + public _cellDict: NumberDictionary = new NumberDictionary(); + /** + * 用于返回冲突信息的共享HashSet + */ + public _tempHashSet: Collider[] = []; - constructor(cellSize: number = 100) { - this._cellSize = cellSize; - this._inverseCellSize = 1 / this._cellSize; - this._raycastParser = new RaycastResultParser(); - } - - /** - * 从SpatialHash中删除对象 - * @param collider - */ - public remove(collider: Collider) { - let bounds = collider.registeredPhysicsBounds; - let p1 = this.cellCoords(bounds.x, bounds.y); - let p2 = this.cellCoords(bounds.right, bounds.bottom); - - for (let x = p1.x; x <= p2.x; x++) { - for (let y = p1.y; y <= p2.y; y++) { - // 单元格应该始终存在,因为这个碰撞器应该在所有查询的单元格中 - let cell = this.cellAtPosition(x, y); - if (!cell) - console.error(`removing Collider [${collider}] from a cell that it is not present in`); - else - cell.remove(collider); - } - } - } - - /** - * 将对象添加到SpatialHash - * @param collider - */ - public register(collider: Collider) { - let bounds = collider.bounds; - collider.registeredPhysicsBounds = bounds; - let p1 = this.cellCoords(bounds.x, bounds.y); - let p2 = this.cellCoords(bounds.right, bounds.bottom); - - // 更新边界以跟踪网格大小 - if (!this.gridBounds.contains(p1.x, p1.y)) { - this.gridBounds = RectangleExt.union(this.gridBounds, p1); + constructor(cellSize: number = 100) { + this._cellSize = cellSize; + this._inverseCellSize = 1 / this._cellSize; + this._raycastParser = new RaycastResultParser(); } - if (!this.gridBounds.contains(p2.x, p2.y)) { - this.gridBounds = RectangleExt.union(this.gridBounds, p2); + /** + * 获取单元格的x,y值作为世界空间的x,y值 + * @param x + * @param y + */ + private cellCoords(x: number, y: number): Vector2 { + return new Vector2(Math.floor(x * this._inverseCellSize), Math.floor(y * this._inverseCellSize)); } - 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) - c.push(collider); - } - } - } - - public clear(){ - this._cellDict.clear(); - } - - /** - * 获取位于指定圆内的所有碰撞器 - * @param circleCenter - * @param radius - * @param results - * @param layerMask - */ - public overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask) { - let bounds = new Rectangle(circleCenter.x - radius, circleCenter.y - radius, radius * 2, radius * 2); - - this._overlapTestCircle.radius = radius; - // this._overlapTestCircle.position = circleCenter; - - let resultCounter = 0; - let aabbBroadphaseResult = this.aabbBroadphase(bounds, null, layerMask); - bounds = aabbBroadphaseResult.bounds; - let potentials = aabbBroadphaseResult.tempHashSet; - for (let i = 0; i < potentials.length; i++) { - let collider = potentials[i]; - if (collider instanceof BoxCollider) { - results[resultCounter] = collider; - resultCounter++; - } else if (collider instanceof CircleCollider) { - if (collider.shape.overlaps(this._overlapTestCircle)){ - results[resultCounter] = collider; - resultCounter ++; + /** + * 获取世界空间x,y值的单元格。 + * 如果单元格为空且createCellIfEmpty为true,则会创建一个新的单元格 + * @param x + * @param y + * @param createCellIfEmpty + */ + private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false) { + let cell: Collider[] = this._cellDict.tryGetValue(x, y); + if (!cell) { + if (createCellIfEmpty) { + cell = []; + this._cellDict.add(x, y, cell); } - } else if(collider instanceof PolygonCollider) { - if (collider.shape.overlaps(this._overlapTestCircle)){ - results[resultCounter] = collider; - resultCounter ++; - } - } else { - throw new Error("overlapCircle against this collider type is not implemented!"); } - - // 如果我们所有的结果数据有了则返回 - if (resultCounter == results.length) - return resultCounter; + return cell; } - return resultCounter; - } + /** + * 将对象添加到SpatialHash + * @param collider + */ + public register(collider: Collider) { + let bounds = collider.bounds; + collider.registeredPhysicsBounds = bounds; + let p1 = this.cellCoords(bounds.x, bounds.y); + let p2 = this.cellCoords(bounds.right, bounds.bottom); - /** - * 返回边框与单元格相交的所有对象 - * @param bounds - * @param excludeCollider - * @param layerMask - */ - public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number) { - this._tempHashSet.length = 0; + // 更新边界以跟踪网格大小 + if (!this.gridBounds.contains(p1.x, p1.y)) { + this.gridBounds = RectangleExt.union(this.gridBounds, p1); + } - let p1 = this.cellCoords(bounds.x, bounds.y); - let p2 = this.cellCoords(bounds.right, bounds.bottom); + if (!this.gridBounds.contains(p2.x, p2.y)) { + this.gridBounds = RectangleExt.union(this.gridBounds, p2); + } - for (let x = p1.x; x <= p2.x; x++) { - for (let y = p1.y; y <= p2.y; y++) { - let cell = this.cellAtPosition(x, y); - if (!cell) - continue; + 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) + c.push(collider); + } + } + } - // 当cell不为空。循环并取回所有碰撞器 - for (let i = 0; i < cell.length; i++) { - let collider = cell[i]; + /** + * 从SpatialHash中删除对象 + * @param collider + */ + public remove(collider: Collider) { + let bounds = collider.registeredPhysicsBounds; + let p1 = this.cellCoords(bounds.x, bounds.y); + let p2 = this.cellCoords(bounds.right, bounds.bottom); - // 如果它是自身或者如果它不匹配我们的层掩码 跳过这个碰撞器 - if (collider == excludeCollider || !Flags.isFlagSet(layerMask, collider.physicsLayer)) + for (let x = p1.x; x <= p2.x; x++) { + for (let y = p1.y; y <= p2.y; y++) { + // 单元格应该始终存在,因为这个碰撞器应该在所有查询的单元格中 + let cell = this.cellAtPosition(x, y); + if (!cell) + console.error(`removing Collider [${collider}] from a cell that it is not present in`); + else + cell.remove(collider); + } + } + } + + /** + * 使用蛮力方法从SpatialHash中删除对象 + * @param obj + */ + public removeWithBruteForce(obj: Collider){ + this._cellDict.remove(obj); + } + + public clear(){ + this._cellDict.clear(); + } + + /** + * debug绘制空间散列的内容 + * @param secondsToDisplay + * @param textScale + */ + public debugDraw(secondsToDisplay: number, textScale: number = 1){ + for (let x = this.gridBounds.x; x <= this.gridBounds.right; x ++){ + for (let y = this.gridBounds.y; y <= this.gridBounds.bottom; y ++){ + let cell = this.cellAtPosition(x, y); + if (cell && cell.length > 0) + this.debugDrawCellDetails(x, y, cell.length, secondsToDisplay, textScale); + } + } + } + + private debugDrawCellDetails(x: number, y: number, cellCount: number, secondsToDisplay = 0.5, textScale = 1){ + + } + + /** + * 返回边框与单元格相交的所有对象 + * @param bounds + * @param excludeCollider + * @param layerMask + */ + public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number) { + this._tempHashSet.length = 0; + + let p1 = this.cellCoords(bounds.x, bounds.y); + let p2 = this.cellCoords(bounds.right, bounds.bottom); + + for (let x = p1.x; x <= p2.x; x++) { + for (let y = p1.y; y <= p2.y; y++) { + let cell = this.cellAtPosition(x, y); + if (!cell) continue; - if (bounds.intersects(collider.bounds)){ - if (this._tempHashSet.indexOf(collider) == -1) - this._tempHashSet.push(collider); + // 当cell不为空。循环并取回所有碰撞器 + for (let i = 0; i < cell.length; i++) { + let collider = cell[i]; + + // 如果它是自身或者如果它不匹配我们的层掩码 跳过这个碰撞器 + if (collider == excludeCollider || !Flags.isFlagSet(layerMask, collider.physicsLayer)) + continue; + + if (bounds.intersects(collider.bounds)){ + if (this._tempHashSet.indexOf(collider) == -1) + this._tempHashSet.push(collider); + } } } } + + return {tempHashSet: this._tempHashSet, bounds: bounds}; } - return {tempHashSet: this._tempHashSet, bounds: bounds}; - } + /** + * 获取位于指定圆内的所有碰撞器 + * @param circleCenter + * @param radius + * @param results + * @param layerMask + */ + public overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask) { + let bounds = new Rectangle(circleCenter.x - radius, circleCenter.y - radius, radius * 2, radius * 2); - /** - * 获取世界空间x,y值的单元格。 - * 如果单元格为空且createCellIfEmpty为true,则会创建一个新的单元格 - * @param x - * @param y - * @param createCellIfEmpty - */ - private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false) { - let cell: Collider[] = this._cellDict.tryGetValue(x, y); - if (!cell) { - if (createCellIfEmpty) { - cell = []; - this._cellDict.add(x, y, cell); + this._overlapTestCircle.radius = radius; + this._overlapTestCircle.position = circleCenter; + + let resultCounter = 0; + let aabbBroadphaseResult = this.aabbBroadphase(bounds, null, layerMask); + bounds = aabbBroadphaseResult.bounds; + let potentials = aabbBroadphaseResult.tempHashSet; + for (let i = 0; i < potentials.length; i++) { + let collider = potentials[i]; + if (collider instanceof BoxCollider) { + results[resultCounter] = collider; + resultCounter++; + } else if (collider instanceof CircleCollider) { + if (collider.shape.overlaps(this._overlapTestCircle)){ + results[resultCounter] = collider; + resultCounter ++; + } + } else if(collider instanceof PolygonCollider) { + if (collider.shape.overlaps(this._overlapTestCircle)){ + results[resultCounter] = collider; + resultCounter ++; + } + } else { + throw new Error("overlapCircle against this collider type is not implemented!"); + } + + // 如果我们所有的结果数据有了则返回 + if (resultCounter == results.length) + return resultCounter; } - } - return cell; - } - /** - * 获取单元格的x,y值作为世界空间的x,y值 - * @param x - * @param y - */ - private cellCoords(x: number, y: number): Vector2 { - return new Vector2(Math.floor(x * this._inverseCellSize), Math.floor(y * this._inverseCellSize)); - } - - /** - * debug绘制空间散列的内容 - * @param secondsToDisplay - * @param textScale - */ - public debugDraw(secondsToDisplay: number, textScale: number = 1){ - for (let x = this.gridBounds.x; x <= this.gridBounds.right; x ++){ - for (let y = this.gridBounds.y; y <= this.gridBounds.bottom; y ++){ - let cell = this.cellAtPosition(x, y); - if (cell && cell.length > 0) - this.debugDrawCellDetails(x, y, cell.length, secondsToDisplay, textScale); - } + return resultCounter; } } - private debugDrawCellDetails(x: number, y: number, cellCount: number, secondsToDisplay = 0.5, textScale = 1){ - + /** + * 包装一个Unit32,列表碰撞器字典 + * 它的主要目的是将int、int x、y坐标散列到单个Uint32键中,使用O(1)查找。 + */ + export class NumberDictionary { + public _store: Map = new Map(); + + /** + * 根据x和y值计算并返回散列键 + * @param x + * @param y + */ + private getKey(x: number, y: number): string { + return Long.fromNumber(x).shiftLeft(32).or(Long.fromNumber(y, false)).toString(); + } + + public add(x: number, y: number, list: Collider[]) { + this._store.set(this.getKey(x, y), list); + } + + /** + * 使用蛮力方法从字典存储列表中移除碰撞器 + * @param obj + */ + public remove(obj: Collider) { + this._store.forEach(list => { + if (list.contains(obj)) + list.remove(obj); + }) + } + + public tryGetValue(x: number, y: number): Collider[] { + return this._store.get(this.getKey(x, y)); + } + + /** + * 清除字典数据 + */ + public clear() { + this._store.clear(); + } + } + + export class RaycastResultParser { + } } - -class RaycastResultParser { - -} - -/** - * 包装一个Unit32,列表碰撞器字典 - * 它的主要目的是将int、int x、y坐标散列到单个Uint32键中,使用O(1)查找。 - */ -class NumberDictionary { - private _store: Map = new Map(); - - /** - * 根据x和y值计算并返回散列键 - * @param x - * @param y - */ - private getKey(x: number, y: number): string { - return Long.fromNumber(x).shiftLeft(32).or(Long.fromNumber(y, false)).toString(); - } - - public add(x: number, y: number, list: Collider[]) { - this._store.set(this.getKey(x, y), list); - } - - /** - * 使用蛮力方法从字典存储列表中移除碰撞器 - * @param obj - */ - public remove(obj: Collider) { - this._store.forEach(list => { - if (list.contains(obj)) - list.remove(obj); - }) - } - - public tryGetValue(x: number, y: number): Collider[] { - return this._store.get(this.getKey(x, y)); - } - - /** - * 清除字典数据 - */ - public clear() { - this._store.clear(); - } -} \ No newline at end of file