新增碰撞检测算法
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
class Entity {
|
class Entity {
|
||||||
|
private static _idGenerator: number;
|
||||||
|
|
||||||
public name: string;
|
public name: string;
|
||||||
|
public readonly id: number;
|
||||||
/** 当前实体所属的场景 */
|
/** 当前实体所属的场景 */
|
||||||
public scene: Scene;
|
public scene: Scene;
|
||||||
/** 封装实体的位置/旋转/缩放,并允许设置一个高层结构 */
|
/** 封装实体的位置/旋转/缩放,并允许设置一个高层结构 */
|
||||||
@@ -9,6 +12,7 @@ class Entity {
|
|||||||
private _updateOrder: number = 0;
|
private _updateOrder: number = 0;
|
||||||
private _enabled: boolean = true;
|
private _enabled: boolean = true;
|
||||||
private _isDestoryed: boolean;
|
private _isDestoryed: boolean;
|
||||||
|
private _tag: number = 0;
|
||||||
|
|
||||||
public componentBits: BitSet;
|
public componentBits: BitSet;
|
||||||
|
|
||||||
@@ -116,10 +120,20 @@ class Entity {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get tag(){
|
||||||
|
return this._tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set tag(value: number){
|
||||||
|
this.setTag(value);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(name: string){
|
constructor(name: string){
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.transform = new Transform(this);
|
this.transform = new Transform(this);
|
||||||
this.components = new ComponentList(this);
|
this.components = new ComponentList(this);
|
||||||
|
this.id = Entity._idGenerator ++;
|
||||||
|
|
||||||
this.componentBits = new BitSet();
|
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){
|
public attachToScene(newScene: Scene){
|
||||||
this.scene = newScene;
|
this.scene = newScene;
|
||||||
newScene.entities.add(this);
|
newScene.entities.add(this);
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ class EntityList{
|
|||||||
private _entitiesToAdded: Entity[] = [];
|
private _entitiesToAdded: Entity[] = [];
|
||||||
private _tempEntityList: Entity[] = [];
|
private _tempEntityList: Entity[] = [];
|
||||||
private _entities: Entity[] = [];
|
private _entities: Entity[] = [];
|
||||||
|
private _entityDict: Map<number, Entity[]> = new Map<number, Entity[]>();
|
||||||
|
private _unsortedTags: number[] = [];
|
||||||
|
|
||||||
constructor(scene: Scene){
|
constructor(scene: Scene){
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
@@ -40,6 +42,31 @@ class EntityList{
|
|||||||
return this._entitiesToAdded.firstOrDefault(entity => entity.name == name);
|
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(){
|
public update(){
|
||||||
for (let i = 0; i < this._entities.length; i++){
|
for (let i = 0; i < this._entities.length; i++){
|
||||||
let entity = this._entities[i];
|
let entity = this._entities[i];
|
||||||
@@ -58,6 +85,7 @@ class EntityList{
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._entities.length = 0;
|
this._entities.length = 0;
|
||||||
|
this._entityDict.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateLists(){
|
public updateLists(){
|
||||||
@@ -89,5 +117,13 @@ class EntityList{
|
|||||||
this._tempEntityList.forEach(entity => entity.onAddedToScene());
|
this._tempEntityList.forEach(entity => entity.onAddedToScene());
|
||||||
this._tempEntityList.length = 0;
|
this._tempEntityList.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._unsortedTags.length > 0){
|
||||||
|
this._unsortedTags.forEach(tag => {
|
||||||
|
this._entityDict.get(tag).sort();
|
||||||
|
});
|
||||||
|
|
||||||
|
this._unsortedTags.length = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
13
source/src/Math/Rectangle.ts
Normal file
13
source/src/Math/Rectangle.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,15 @@ class Vector2 {
|
|||||||
public x: number = 0;
|
public x: number = 0;
|
||||||
public y: 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(){
|
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;
|
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){
|
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));
|
return new Vector2((position.x * matrix.m11) + (position.y * matrix.m21), (position.x * matrix.m12) + (position.y * matrix.m22));
|
||||||
}
|
}
|
||||||
|
|||||||
198
source/src/Physics/Collision.ts
Normal file
198
source/src/Physics/Collision.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user