diff --git a/source/src/ECS/Entity.ts b/source/src/ECS/Entity.ts index 4c61fe85..03e2911b 100644 --- a/source/src/ECS/Entity.ts +++ b/source/src/ECS/Entity.ts @@ -1,5 +1,8 @@ class Entity { + private static _idGenerator: number; + public name: string; + public readonly id: number; /** 当前实体所属的场景 */ public scene: Scene; /** 封装实体的位置/旋转/缩放,并允许设置一个高层结构 */ @@ -9,6 +12,7 @@ class Entity { private _updateOrder: number = 0; private _enabled: boolean = true; private _isDestoryed: boolean; + private _tag: number = 0; public componentBits: BitSet; @@ -116,10 +120,20 @@ class Entity { return this; } + public get tag(){ + return this._tag; + } + + public set tag(value: number){ + this.setTag(value); + } + constructor(name: string){ this.name = name; this.transform = new Transform(this); this.components = new ComponentList(this); + this.id = Entity._idGenerator ++; + this.componentBits = new BitSet(); } @@ -142,6 +156,20 @@ class Entity { } } + public setTag(tag: number): Entity{ + if (this._tag != tag){ + if (this.scene){ + this.scene.entities.removeFromTagList(this); + } + this._tag = tag; + if (this.scene){ + this.scene.entities.addToTagList(this); + } + } + + return this; + } + public attachToScene(newScene: Scene){ this.scene = newScene; newScene.entities.add(this); diff --git a/source/src/ECS/Utils/EntityList.ts b/source/src/ECS/Utils/EntityList.ts index d3e20f73..5e19ceb2 100644 --- a/source/src/ECS/Utils/EntityList.ts +++ b/source/src/ECS/Utils/EntityList.ts @@ -4,6 +4,8 @@ class EntityList{ private _entitiesToAdded: Entity[] = []; private _tempEntityList: Entity[] = []; private _entities: Entity[] = []; + private _entityDict: Map = new Map(); + private _unsortedTags: number[] = []; constructor(scene: Scene){ this.scene = scene; @@ -40,6 +42,31 @@ class EntityList{ return this._entitiesToAdded.firstOrDefault(entity => entity.name == name); } + public getTagList(tag: number){ + let list = this._entityDict.get(tag); + if (!list){ + list = []; + this._entityDict.set(tag, list); + } + + return this._entityDict.get(tag); + } + + public addToTagList(entity: Entity){ + let list = this.getTagList(entity.tag); + if (!list.contains(entity)){ + list.push(entity); + this._unsortedTags.push(entity.tag); + } + } + + public removeFromTagList(entity: Entity){ + let list = this._entityDict.get(entity.tag); + if (list){ + list.remove(entity); + } + } + public update(){ for (let i = 0; i < this._entities.length; i++){ let entity = this._entities[i]; @@ -58,6 +85,7 @@ class EntityList{ } this._entities.length = 0; + this._entityDict.clear(); } public updateLists(){ @@ -89,5 +117,13 @@ class EntityList{ this._tempEntityList.forEach(entity => entity.onAddedToScene()); this._tempEntityList.length = 0; } + + if (this._unsortedTags.length > 0){ + this._unsortedTags.forEach(tag => { + this._entityDict.get(tag).sort(); + }); + + this._unsortedTags.length = 0; + } } } \ No newline at end of file diff --git a/source/src/Math/Rectangle.ts b/source/src/Math/Rectangle.ts new file mode 100644 index 00000000..d88cdb9a --- /dev/null +++ b/source/src/Math/Rectangle.ts @@ -0,0 +1,13 @@ +class Rectangle { + public x: number; + public y: number; + public width: number; + public height: number; + + constructor(x: number, y: number, width: number, height: number){ + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } +} \ No newline at end of file diff --git a/source/src/Math/Vector2.ts b/source/src/Math/Vector2.ts index 15ea5983..438e66d7 100644 --- a/source/src/Math/Vector2.ts +++ b/source/src/Math/Vector2.ts @@ -3,10 +3,15 @@ class Vector2 { public x: number = 0; public y: number = 0; - private static readonly unitVector2 = new Vector2(1, 1); + private static readonly zeroVector = new Vector2(0, 0); + private static readonly unitVector = new Vector2(1, 1); public static get One(){ - return this.unitVector2; + return this.unitVector; + } + + public static get Zero(){ + return this.zeroVector; } /** @@ -50,6 +55,25 @@ class Vector2 { this.y *= val; } + /** + * 返回两个向量的点积 + * @param value1 + * @param value2 + */ + public static dot(value1: Vector2, value2: Vector2): number{ + return (value1.x * value2.x) + (value1.y * value2.y); + } + + /** + * 返回两个向量之间距离的平方 + * @param value1 + * @param value2 + */ + public static distanceSquared(value1: Vector2, value2: Vector2){ + let v1 = value1.x - value2.x, v2 = value1.y - value2.y; + return (v1 * v1) + (v2 * v2); + } + public static transform(position: Vector2, matrix: Matrix2D){ return new Vector2((position.x * matrix.m11) + (position.y * matrix.m21), (position.x * matrix.m12) + (position.y * matrix.m22)); } diff --git a/source/src/Physics/Collision.ts b/source/src/Physics/Collision.ts new file mode 100644 index 00000000..bef32cbb --- /dev/null +++ b/source/src/Physics/Collision.ts @@ -0,0 +1,198 @@ +enum PointSectors { + center = 0, + top = 1, + bottom = 2, + topLeft = 9, + topRight = 5, + left = 8, + right = 4, + bottomLeft = 10, + bottomRight = 6 +} + +class Collisions { + public static isLineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): boolean { + let b = Vector2.subtract(a2, a1); + let d = Vector2.subtract(b2, b1); + let bDotDPerp = b.x * d.y - b.y * d.x; + + // 如果b*d = 0,表示这两条直线平行,因此有无穷个交点 + if (bDotDPerp == 0) + return false; + + let c = Vector2.subtract(b1, a1); + let t = (c.x * d.y - c.y * d.x) / bDotDPerp; + if (t < 0 || t > 1) + return false; + + let u = (c.x * b.y - c.y * b.x) / bDotDPerp; + if (u < 0 || u > 1) + return false; + + return true; + } + + public static lineToLineIntersection(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): Vector2 { + let intersection = Vector2.Zero; + + let b = Vector2.subtract(a2, a1); + let d = Vector2.subtract(b2, b1); + let bDotDPerp = b.x * d.y - b.y * d.x; + + // 如果b*d = 0,表示这两条直线平行,因此有无穷个交点 + if (bDotDPerp == 0) + return intersection; + + let c = Vector2.subtract(b1, a1); + let t = (c.x * d.y - c.y * d.x) / bDotDPerp; + if (t < 0 || t > 1) + return intersection; + + let u = (c.x * b.y - c.y * b.x) / bDotDPerp; + if (u < 0 || u > 1) + return intersection; + + intersection = Vector2.add(a1, new Vector2(t * b.x, t * b.y)); + + return intersection; + } + + public static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2) { + let v = Vector2.subtract(lineB, lineA); + let w = Vector2.subtract(closestTo, lineA); + let t = Vector2.dot(w, v) / Vector2.dot(v, v); + t = MathHelper.clamp(t, 0, 1); + + return Vector2.add(lineA, new Vector2(v.x * t, v.y * t)); + } + + public static isCircleToCircle(circleCenter1: Vector2, circleRadius1: number, circleCenter2: Vector2, circleRadius2: number): boolean { + return Vector2.distanceSquared(circleCenter1, circleCenter2) < (circleRadius1 + circleRadius2) * (circleRadius1 + circleRadius2); + } + + public static isCircleToLine(circleCenter: Vector2, radius: number, lineFrom: Vector2, lineTo: Vector2): boolean { + return Vector2.distanceSquared(circleCenter, this.closestPointOnLine(lineFrom, lineTo, circleCenter)) < radius * radius; + } + + public static isCircleToPoint(circleCenter: Vector2, radius: number, point: Vector2): boolean { + return Vector2.distanceSquared(circleCenter, point) < radius * radius; + } + + public static isRectToCircle(rect: Rectangle, cPosition: Vector2, cRadius: number): boolean { + if (this.isRectToPoint(rect.x, rect.y, rect.width, rect.height, cPosition)) + return true; + + let edgeFrom: Vector2; + let edgeTo: Vector2; + let sector = this.getSector(rect.x, rect.y, rect.width, rect.height, cPosition); + + if ((sector & PointSectors.top) != 0) { + edgeFrom = new Vector2(rect.x, rect.y); + edgeTo = new Vector2(rect.x + rect.width, rect.y); + if (this.isCircleToLine(cPosition, cRadius, edgeFrom, edgeTo)) + return true; + } + + if ((sector & PointSectors.bottom) != 0) { + edgeFrom = new Vector2(rect.x, rect.y + rect.height); + edgeTo = new Vector2(rect.x + rect.width, rect.y + rect.height); + if (this.isCircleToLine(cPosition, cRadius, edgeFrom, edgeTo)) + return true; + } + + if ((sector & PointSectors.left) != 0) { + edgeFrom = new Vector2(rect.x, rect.y); + edgeTo = new Vector2(rect.x, rect.y + rect.height); + if (this.isCircleToLine(cPosition, cRadius, edgeFrom, edgeTo)) + return true; + } + + if ((sector & PointSectors.right) != 0) { + edgeFrom = new Vector2(rect.x + rect.width, rect.y); + edgeTo = new Vector2(rect.x + rect.width, rect.y + rect.height); + if (this.isCircleToLine(cPosition, cRadius, edgeFrom, edgeTo)) + return true; + } + + return false; + } + + public static isRectToLine(rect: Rectangle, lineFrom: Vector2, lineTo: Vector2){ + let fromSector = this.getSector(rect.x, rect.y, rect.width, rect.height, lineFrom); + let toSector = this.getSector(rect.x, rect.y, rect.width, rect.height, lineTo); + + if (fromSector == PointSectors.center || toSector == PointSectors.center){ + return true; + } else if((fromSector & toSector) != 0){ + return false; + } else{ + let both = fromSector | toSector; + // 线对边进行检查 + let edgeFrom: Vector2; + let edgeTo: Vector2; + + if ((both & PointSectors.top) != 0){ + edgeFrom = new Vector2(rect.x, rect.y); + edgeTo = new Vector2(rect.x + rect.width, rect.y); + if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo)) + return true; + } + + if ((both & PointSectors.bottom) != 0){ + edgeFrom = new Vector2(rect.x, rect.y + rect.height); + edgeTo = new Vector2(rect.x + rect.width, rect.y + rect.height); + if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo)) + return true; + } + + if ((both & PointSectors.left) != 0){ + edgeFrom = new Vector2(rect.x, rect.y); + edgeTo = new Vector2(rect.x, rect.y + rect.height); + if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo)) + return true; + } + + if ((both & PointSectors.right) != 0){ + edgeFrom = new Vector2(rect.x + rect.width, rect.y); + edgeTo = new Vector2(rect.x + rect.width, rect.y + rect.height); + if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo)) + return true; + } + } + + return false; + } + + public static isRectToPoint(rX: number, rY: number, rW: number, rH: number, point: Vector2) { + return point.x >= rX && point.y >= rY && point.x < rX + rW && point.y < rY + rH; + } + + /** + * 位标志和帮助使用Cohen–Sutherland算法 + * + * 位标志: + * 1001 1000 1010 + * 0001 0000 0010 + * 0101 0100 0110 + * @param rX + * @param rY + * @param rW + * @param rH + * @param point + */ + public static getSector(rX: number, rY: number, rW: number, rH: number, point: Vector2): PointSectors { + let sector = PointSectors.center; + + if (point.x < rX) + sector |= PointSectors.left; + else if (point.x >= rX + rW) + sector |= PointSectors.right; + + if (point.y < rY) + sector |= PointSectors.top; + else if (point.y >= rY + rH) + sector |= PointSectors.bottom; + + return sector; + } +} \ No newline at end of file