2020-07-23 09:10:27 +08:00
|
|
|
|
module es {
|
|
|
|
|
|
export class SpatialHash {
|
|
|
|
|
|
public gridBounds: Rectangle = new Rectangle();
|
|
|
|
|
|
|
|
|
|
|
|
public _raycastParser: RaycastResultParser;
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 散列中每个单元格的大小
|
|
|
|
|
|
*/
|
|
|
|
|
|
public _cellSize: number;
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 1除以单元格大小。缓存结果,因为它被大量使用。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public _inverseCellSize: number;
|
|
|
|
|
|
/**
|
2020-12-15 11:46:33 +08:00
|
|
|
|
* 重叠检查缓存框
|
|
|
|
|
|
*/
|
|
|
|
|
|
public _overlapTestBox: Box = new Box(0, 0);
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 重叠检查缓存圈
|
2020-07-23 09:10:27 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public _overlapTestCircle: Circle = new Circle(0);
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 保存所有数据的字典
|
|
|
|
|
|
*/
|
|
|
|
|
|
public _cellDict: NumberDictionary = new NumberDictionary();
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 用于返回冲突信息的共享HashSet
|
|
|
|
|
|
*/
|
2020-11-26 17:26:49 +08:00
|
|
|
|
public _tempHashSet: Set<Collider> = new Set<Collider>();
|
2020-07-23 09:10:27 +08:00
|
|
|
|
|
|
|
|
|
|
constructor(cellSize: number = 100) {
|
|
|
|
|
|
this._cellSize = cellSize;
|
|
|
|
|
|
this._inverseCellSize = 1 / this._cellSize;
|
|
|
|
|
|
this._raycastParser = new RaycastResultParser();
|
2020-06-15 10:42:06 +08:00
|
|
|
|
}
|
2020-06-16 11:59:40 +08:00
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 将对象添加到SpatialHash
|
|
|
|
|
|
* @param collider
|
|
|
|
|
|
*/
|
|
|
|
|
|
public register(collider: Collider) {
|
2020-12-03 17:58:25 +08:00
|
|
|
|
let bounds = collider.bounds.clone();
|
2020-07-23 09:10:27 +08:00
|
|
|
|
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);
|
2020-06-16 11:59:40 +08:00
|
|
|
|
}
|
2020-06-15 10:42:06 +08:00
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
if (!this.gridBounds.contains(p2.x, p2.y)) {
|
|
|
|
|
|
this.gridBounds = RectangleExt.union(this.gridBounds, p2);
|
|
|
|
|
|
}
|
2020-06-21 10:27:15 +08:00
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
for (let x = p1.x; x <= p2.x; x++) {
|
|
|
|
|
|
for (let y = p1.y; y <= p2.y; y++) {
|
|
|
|
|
|
// 如果没有单元格,我们需要创建它
|
2020-07-31 19:33:04 +08:00
|
|
|
|
let c: Collider[] = this.cellAtPosition(x, y, true);
|
2020-11-26 17:26:49 +08:00
|
|
|
|
c.push(collider);
|
2020-07-09 15:11:30 +08:00
|
|
|
|
}
|
2020-06-16 16:35:17 +08:00
|
|
|
|
}
|
2020-07-23 09:10:27 +08:00
|
|
|
|
}
|
2020-06-16 16:35:17 +08:00
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 从SpatialHash中删除对象
|
|
|
|
|
|
* @param collider
|
|
|
|
|
|
*/
|
|
|
|
|
|
public remove(collider: Collider) {
|
2020-12-03 17:58:25 +08:00
|
|
|
|
let bounds = collider.registeredPhysicsBounds.clone();
|
2020-07-23 09:10:27 +08:00
|
|
|
|
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);
|
2021-01-18 19:54:41 +08:00
|
|
|
|
Insist.isNotNull(cell, `从不存在碰撞器的单元格中移除碰撞器: [${collider}]`);
|
|
|
|
|
|
if (cell != null)
|
2021-03-29 15:28:18 +08:00
|
|
|
|
new es.List(cell).remove(collider);
|
2020-07-23 09:10:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-06-16 16:35:17 +08:00
|
|
|
|
}
|
2020-06-11 00:03:26 +08:00
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 使用蛮力方法从SpatialHash中删除对象
|
|
|
|
|
|
* @param obj
|
|
|
|
|
|
*/
|
2020-07-28 16:25:20 +08:00
|
|
|
|
public removeWithBruteForce(obj: Collider) {
|
2020-07-23 09:10:27 +08:00
|
|
|
|
this._cellDict.remove(obj);
|
|
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
public clear() {
|
2020-07-23 09:10:27 +08:00
|
|
|
|
this._cellDict.clear();
|
|
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
|
2021-05-27 18:32:38 +08:00
|
|
|
|
public debugDraw(secondsToDisplay: number) {
|
|
|
|
|
|
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 != null && cell.length > 0)
|
|
|
|
|
|
this.debugDrawCellDetails(x, y, secondsToDisplay);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private debugDrawCellDetails(x: number, y: number, secondsToDisplay: number = 0.5) {
|
|
|
|
|
|
Graphics.instance.batcher.drawHollowRect(x * this._cellSize, y * this._cellSize, this._cellSize, this._cellSize, new Color(255, 0, 0), secondsToDisplay);
|
|
|
|
|
|
Graphics.instance.batcher.end();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 返回边框与单元格相交的所有对象
|
|
|
|
|
|
* @param bounds
|
|
|
|
|
|
* @param excludeCollider
|
|
|
|
|
|
* @param layerMask
|
|
|
|
|
|
*/
|
2020-11-26 17:26:49 +08:00
|
|
|
|
public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number): Set<Collider> {
|
|
|
|
|
|
this._tempHashSet.clear();
|
2020-07-23 09:10:27 +08:00
|
|
|
|
|
|
|
|
|
|
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);
|
2020-12-03 17:58:25 +08:00
|
|
|
|
if (cell == null)
|
2020-06-12 08:47:13 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
// 当cell不为空。循环并取回所有碰撞器
|
|
|
|
|
|
for (let i = 0; i < cell.length; i++) {
|
|
|
|
|
|
let collider = cell[i];
|
|
|
|
|
|
|
|
|
|
|
|
// 如果它是自身或者如果它不匹配我们的层掩码 跳过这个碰撞器
|
2020-08-27 18:48:20 +08:00
|
|
|
|
if (collider == excludeCollider || !Flags.isFlagSet(layerMask, collider.physicsLayer.value))
|
2020-07-23 09:10:27 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (bounds.intersects(collider.bounds)) {
|
2020-11-26 17:26:49 +08:00
|
|
|
|
this._tempHashSet.add(collider);
|
2020-07-23 09:10:27 +08:00
|
|
|
|
}
|
2020-06-18 09:06:59 +08:00
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-07-23 09:10:27 +08:00
|
|
|
|
|
2020-07-27 16:10:36 +08:00
|
|
|
|
return this._tempHashSet;
|
2020-06-12 08:47:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-22 12:21:40 +08:00
|
|
|
|
/**
|
2020-12-15 11:46:33 +08:00
|
|
|
|
* 通过空间散列投掷一条线,并将该线碰到的任何碰撞器填入碰撞数组
|
|
|
|
|
|
* https://github.com/francisengelmann/fast_voxel_traversal/blob/master/main.cpp
|
|
|
|
|
|
* http://www.cse.yorku.ca/~amana/research/grid.pdf
|
2020-08-22 12:21:40 +08:00
|
|
|
|
* @param start
|
|
|
|
|
|
* @param end
|
|
|
|
|
|
* @param hits
|
|
|
|
|
|
* @param layerMask
|
|
|
|
|
|
*/
|
|
|
|
|
|
public linecast(start: Vector2, end: Vector2, hits: RaycastHit[], layerMask: number){
|
|
|
|
|
|
let ray = new Ray2D(start, end);
|
|
|
|
|
|
this._raycastParser.start(ray, hits, layerMask);
|
|
|
|
|
|
|
2020-12-15 11:46:33 +08:00
|
|
|
|
// 获取我们的起始/结束位置,与我们的网格在同一空间内
|
2020-08-22 12:21:40 +08:00
|
|
|
|
let currentCell = this.cellCoords(start.x, start.y);
|
|
|
|
|
|
let lastCell = this.cellCoords(end.x, end.y);
|
|
|
|
|
|
|
2020-12-15 11:46:33 +08:00
|
|
|
|
// 我们向什么方向递增单元格检查?
|
2020-08-22 12:21:40 +08:00
|
|
|
|
let stepX = Math.sign(ray.direction.x);
|
|
|
|
|
|
let stepY = Math.sign(ray.direction.y);
|
|
|
|
|
|
|
2020-12-15 11:46:33 +08:00
|
|
|
|
// 我们要确保,如果我们在同一条线上或同一排上,就不会踩到不必要的方向上
|
2020-08-22 12:21:40 +08:00
|
|
|
|
if (currentCell.x == lastCell.x) stepX = 0;
|
|
|
|
|
|
if (currentCell.y == lastCell.y) stepY = 0;
|
|
|
|
|
|
|
2020-12-15 11:46:33 +08:00
|
|
|
|
// 计算单元格的边界。
|
|
|
|
|
|
// 当步长为正数时,下一个单元格在这个单元格之后,意味着我们要加1。
|
2020-08-22 12:21:40 +08:00
|
|
|
|
let xStep = stepX < 0 ? 0 : stepX;
|
|
|
|
|
|
let yStep = stepY < 0 ? 0 : stepY;
|
|
|
|
|
|
let nextBoundaryX = (currentCell.x + xStep) * this._cellSize;
|
|
|
|
|
|
let nextBoundaryY = (currentCell.y + yStep) * this._cellSize;
|
|
|
|
|
|
|
2020-12-15 11:46:33 +08:00
|
|
|
|
// 确定射线穿过第一个垂直体素边界时的t值,y/水平也是如此。
|
|
|
|
|
|
// 这两个值的最小值将表明我们可以沿着射线移动多少,并且仍然保持在当前的体素中,对于接近垂直/水平的射线可能是无限的。
|
2020-08-22 12:21:40 +08:00
|
|
|
|
let tMaxX = ray.direction.x != 0 ? (nextBoundaryX - ray.start.x) / ray.direction.x : Number.MAX_VALUE;
|
|
|
|
|
|
let tMaxY = ray.direction.y != 0 ? (nextBoundaryY - ray.start.y) / ray.direction.y : Number.MAX_VALUE;
|
|
|
|
|
|
|
2020-12-15 11:46:33 +08:00
|
|
|
|
// 我们要走多远才能从一个单元格的边界穿过一个单元格
|
2020-08-22 12:21:40 +08:00
|
|
|
|
let tDeltaX = ray.direction.x != 0 ? this._cellSize / (ray.direction.x * stepX) : Number.MAX_VALUE;
|
|
|
|
|
|
let tDeltaY = ray.direction.y != 0 ? this._cellSize / (ray.direction.y * stepY) : Number.MAX_VALUE;
|
|
|
|
|
|
|
|
|
|
|
|
// 开始遍历并返回交叉单元格。
|
|
|
|
|
|
let cell = this.cellAtPosition(currentCell.x, currentCell.y);
|
|
|
|
|
|
|
2021-05-27 18:32:38 +08:00
|
|
|
|
if (cell != null && this._raycastParser.checkRayIntersection(currentCell.x, currentCell.y, cell)){
|
2020-08-22 12:21:40 +08:00
|
|
|
|
this._raycastParser.reset();
|
|
|
|
|
|
return this._raycastParser.hitCounter;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while (currentCell.x != lastCell.x || currentCell.y != lastCell.y){
|
|
|
|
|
|
if (tMaxX < tMaxY){
|
2021-05-27 18:32:38 +08:00
|
|
|
|
currentCell.x = MathHelper.approach(currentCell.x, lastCell.x, Math.abs(stepX));
|
2020-08-22 12:21:40 +08:00
|
|
|
|
|
|
|
|
|
|
tMaxX += tDeltaX;
|
|
|
|
|
|
}else{
|
2021-05-27 18:32:38 +08:00
|
|
|
|
currentCell.y = MathHelper.approach(currentCell.y, lastCell.y, Math.abs(stepY));
|
2020-08-22 12:21:40 +08:00
|
|
|
|
|
|
|
|
|
|
tMaxY += tDeltaY;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cell = this.cellAtPosition(currentCell.x, currentCell.y);
|
|
|
|
|
|
if (cell && this._raycastParser.checkRayIntersection(currentCell.x, currentCell.y, cell)){
|
|
|
|
|
|
this._raycastParser.reset();
|
|
|
|
|
|
return this._raycastParser.hitCounter;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 11:46:33 +08:00
|
|
|
|
// 复位
|
2020-08-22 12:21:40 +08:00
|
|
|
|
this._raycastParser.reset();
|
|
|
|
|
|
return this._raycastParser.hitCounter;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
/**
|
2020-12-15 11:46:33 +08:00
|
|
|
|
* 获取所有在指定矩形范围内的碰撞器
|
|
|
|
|
|
* @param rect
|
|
|
|
|
|
* @param results
|
|
|
|
|
|
* @param layerMask
|
|
|
|
|
|
*/
|
|
|
|
|
|
public overlapRectangle(rect: Rectangle, results: Collider[], layerMask: number) {
|
|
|
|
|
|
this._overlapTestBox.updateBox(rect.width, rect.height);
|
2021-05-28 08:41:38 +08:00
|
|
|
|
this._overlapTestBox.position = rect.location.clone();
|
2020-12-15 11:46:33 +08:00
|
|
|
|
|
|
|
|
|
|
let resultCounter = 0;
|
|
|
|
|
|
let potentials = this.aabbBroadphase(rect, null, layerMask);
|
|
|
|
|
|
for (let collider of potentials) {
|
|
|
|
|
|
if (collider instanceof BoxCollider) {
|
|
|
|
|
|
results[resultCounter] = collider;
|
|
|
|
|
|
resultCounter ++;
|
|
|
|
|
|
} else if(collider instanceof CircleCollider) {
|
|
|
|
|
|
if (Collisions.rectToCircle(rect, collider.bounds.center, collider.bounds.width * 0.5)) {
|
|
|
|
|
|
results[resultCounter] = collider;
|
|
|
|
|
|
resultCounter ++;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if(collider instanceof PolygonCollider) {
|
|
|
|
|
|
if (collider.shape.overlaps(this._overlapTestBox)) {
|
|
|
|
|
|
results[resultCounter] = collider;
|
|
|
|
|
|
resultCounter ++;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw new Error("overlapRectangle对这个类型没有实现!");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (resultCounter == results.length)
|
|
|
|
|
|
return resultCounter;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return resultCounter;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取所有落在指定圆圈内的碰撞器
|
2020-07-23 09:10:27 +08:00
|
|
|
|
* @param circleCenter
|
|
|
|
|
|
* @param radius
|
|
|
|
|
|
* @param results
|
|
|
|
|
|
* @param layerMask
|
|
|
|
|
|
*/
|
2020-07-27 16:10:36 +08:00
|
|
|
|
public overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask): number {
|
2020-07-23 09:10:27 +08:00
|
|
|
|
let bounds = new Rectangle(circleCenter.x - radius, circleCenter.y - radius, radius * 2, radius * 2);
|
|
|
|
|
|
|
|
|
|
|
|
this._overlapTestCircle.radius = radius;
|
2021-05-28 08:41:38 +08:00
|
|
|
|
this._overlapTestCircle.position = circleCenter.clone();
|
2020-07-23 09:10:27 +08:00
|
|
|
|
|
|
|
|
|
|
let resultCounter = 0;
|
2020-07-27 16:10:36 +08:00
|
|
|
|
let potentials = this.aabbBroadphase(bounds, null, layerMask);
|
2020-12-04 11:57:54 +08:00
|
|
|
|
for (let collider of potentials) {
|
2020-07-23 09:10:27 +08:00
|
|
|
|
if (collider instanceof BoxCollider) {
|
|
|
|
|
|
results[resultCounter] = collider;
|
|
|
|
|
|
resultCounter++;
|
|
|
|
|
|
} else if (collider instanceof CircleCollider) {
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (collider.shape.overlaps(this._overlapTestCircle)) {
|
2020-07-23 09:10:27 +08:00
|
|
|
|
results[resultCounter] = collider;
|
2020-07-28 16:25:20 +08:00
|
|
|
|
resultCounter++;
|
2020-07-23 09:10:27 +08:00
|
|
|
|
}
|
2020-07-28 16:25:20 +08:00
|
|
|
|
} else if (collider instanceof PolygonCollider) {
|
|
|
|
|
|
if (collider.shape.overlaps(this._overlapTestCircle)) {
|
2020-07-23 09:10:27 +08:00
|
|
|
|
results[resultCounter] = collider;
|
2020-07-28 16:25:20 +08:00
|
|
|
|
resultCounter++;
|
2020-07-23 09:10:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2020-12-04 11:57:54 +08:00
|
|
|
|
throw new Error("对这个对撞机类型的overlapCircle没有实现!");
|
2020-07-23 09:10:27 +08:00
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
// 如果我们所有的结果数据有了则返回
|
|
|
|
|
|
if (resultCounter == results.length)
|
|
|
|
|
|
return resultCounter;
|
2020-12-04 11:57:54 +08:00
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
return resultCounter;
|
|
|
|
|
|
}
|
2020-07-28 16:25:20 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取单元格的x,y值作为世界空间的x,y值
|
|
|
|
|
|
* @param x
|
|
|
|
|
|
* @param y
|
|
|
|
|
|
*/
|
2020-11-26 17:26:49 +08:00
|
|
|
|
public cellCoords(x: number, y: number): Vector2 {
|
2021-05-25 11:16:49 +08:00
|
|
|
|
return new Vector2(MathHelper.floorToInt(x * this._inverseCellSize), MathHelper.floorToInt(y * this._inverseCellSize));
|
2020-07-28 16:25:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取世界空间x,y值的单元格。
|
|
|
|
|
|
* 如果单元格为空且createCellIfEmpty为true,则会创建一个新的单元格
|
|
|
|
|
|
* @param x
|
|
|
|
|
|
* @param y
|
|
|
|
|
|
* @param createCellIfEmpty
|
|
|
|
|
|
*/
|
2020-11-26 17:26:49 +08:00
|
|
|
|
public cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false): Collider[] {
|
2020-07-28 16:25:20 +08:00
|
|
|
|
let cell: Collider[] = this._cellDict.tryGetValue(x, y);
|
|
|
|
|
|
if (!cell) {
|
|
|
|
|
|
if (createCellIfEmpty) {
|
|
|
|
|
|
cell = [];
|
|
|
|
|
|
this._cellDict.add(x, y, cell);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return cell;
|
|
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
}
|
2020-07-09 16:16:04 +08:00
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
export class NumberDictionary {
|
2021-05-28 15:45:45 +08:00
|
|
|
|
public _store: Map<string, Collider[]> = new Map<string, Collider[]>();
|
2020-07-23 09:10:27 +08:00
|
|
|
|
|
|
|
|
|
|
public add(x: number, y: number, list: Collider[]) {
|
|
|
|
|
|
this._store.set(this.getKey(x, y), list);
|
|
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 使用蛮力方法从字典存储列表中移除碰撞器
|
|
|
|
|
|
* @param obj
|
|
|
|
|
|
*/
|
|
|
|
|
|
public remove(obj: Collider) {
|
|
|
|
|
|
this._store.forEach(list => {
|
2021-03-29 15:28:18 +08:00
|
|
|
|
let linqList = new es.List(list);
|
2020-11-30 13:50:18 +08:00
|
|
|
|
if (linqList.contains(obj))
|
|
|
|
|
|
linqList.remove(obj);
|
2020-07-23 09:10:27 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
public tryGetValue(x: number, y: number): Collider[] {
|
|
|
|
|
|
return this._store.get(this.getKey(x, y));
|
|
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
|
2020-08-26 19:56:48 +08:00
|
|
|
|
public getKey(x: number, y: number){
|
2021-05-28 15:45:45 +08:00
|
|
|
|
return `${x}_${y}`;
|
2020-08-26 19:56:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 清除字典数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
public clear() {
|
|
|
|
|
|
this._store.clear();
|
|
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-23 09:10:27 +08:00
|
|
|
|
export class RaycastResultParser {
|
2020-07-31 19:33:04 +08:00
|
|
|
|
public hitCounter: number;
|
|
|
|
|
|
public static compareRaycastHits = (a: RaycastHit, b: RaycastHit) => {
|
|
|
|
|
|
return a.distance - b.distance;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
public _hits: RaycastHit[];
|
2020-08-22 12:21:40 +08:00
|
|
|
|
public _tempHit: RaycastHit = new RaycastHit();
|
2020-07-31 19:33:04 +08:00
|
|
|
|
public _checkedColliders: Collider[] = [];
|
|
|
|
|
|
public _cellHits: RaycastHit[] = [];
|
|
|
|
|
|
public _ray: Ray2D;
|
|
|
|
|
|
public _layerMask: number;
|
|
|
|
|
|
|
2020-08-03 14:45:57 +08:00
|
|
|
|
public start(ray: Ray2D, hits: RaycastHit[], layerMask: number) {
|
2020-07-31 19:33:04 +08:00
|
|
|
|
this._ray = ray;
|
|
|
|
|
|
this._hits = hits;
|
|
|
|
|
|
this._layerMask = layerMask;
|
|
|
|
|
|
this.hitCounter = 0;
|
|
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
|
2020-07-31 19:33:04 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 如果hits数组被填充,返回true。单元格不能为空!
|
|
|
|
|
|
* @param cellX
|
|
|
|
|
|
* @param cellY
|
|
|
|
|
|
* @param cell
|
|
|
|
|
|
*/
|
2020-08-03 14:45:57 +08:00
|
|
|
|
public checkRayIntersection(cellX: number, cellY: number, cell: Collider[]): boolean {
|
2020-08-25 14:21:37 +08:00
|
|
|
|
let fraction: Ref<number> = new Ref(0);
|
2020-08-03 14:45:57 +08:00
|
|
|
|
for (let i = 0; i < cell.length; i++) {
|
2020-07-31 19:33:04 +08:00
|
|
|
|
let potential = cell[i];
|
|
|
|
|
|
|
|
|
|
|
|
// 管理我们已经处理过的碰撞器
|
2021-03-29 15:28:18 +08:00
|
|
|
|
if (new es.List(this._checkedColliders).contains(potential))
|
2020-07-31 19:33:04 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
this._checkedColliders.push(potential);
|
|
|
|
|
|
// 只有当我们被设置为这样做时才会点击触发器
|
|
|
|
|
|
if (potential.isTrigger && !Physics.raycastsHitTriggers)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
// 确保碰撞器在图层蒙版上
|
2020-08-27 18:48:20 +08:00
|
|
|
|
if (!Flags.isFlagSet(this._layerMask, potential.physicsLayer.value))
|
2020-07-31 19:33:04 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: rayIntersects的性能够吗?需要测试它。Collisions.rectToLine可能更快
|
|
|
|
|
|
// TODO: 如果边界检查返回更多数据,我们就不需要为BoxCollider检查做任何事情
|
|
|
|
|
|
// 在做形状测试之前先做一个边界检查
|
2020-12-03 17:58:25 +08:00
|
|
|
|
let colliderBounds = potential.bounds.clone();
|
2020-08-25 14:21:37 +08:00
|
|
|
|
if (colliderBounds.rayIntersects(this._ray, fraction) && fraction.value <= 1){
|
2020-08-03 14:45:57 +08:00
|
|
|
|
if (potential.shape.collidesWithLine(this._ray.start, this._ray.end, this._tempHit)) {
|
|
|
|
|
|
// 检查一下,我们应该排除这些射线,射线cast是否在碰撞器中开始
|
|
|
|
|
|
if (!Physics.raycastsStartInColliders && potential.shape.containsPoint(this._ray.start))
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: 确保碰撞点在当前单元格中,如果它没有保存它以供以后计算
|
|
|
|
|
|
|
|
|
|
|
|
this._tempHit.collider = potential;
|
|
|
|
|
|
this._cellHits.push(this._tempHit);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this._cellHits.length == 0)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
// 所有处理单元完成。对结果进行排序并将命中结果打包到结果数组中
|
|
|
|
|
|
this._cellHits.sort(RaycastResultParser.compareRaycastHits);
|
|
|
|
|
|
for (let i = 0; i < this._cellHits.length; i ++){
|
|
|
|
|
|
this._hits[this.hitCounter] = this._cellHits[i];
|
|
|
|
|
|
|
|
|
|
|
|
// 增加命中计数器,如果它已经达到数组大小的限制,我们就完成了
|
|
|
|
|
|
this.hitCounter ++;
|
|
|
|
|
|
if (this.hitCounter == this._hits.length)
|
|
|
|
|
|
return true;
|
2020-07-31 19:33:04 +08:00
|
|
|
|
}
|
2020-08-03 14:45:57 +08:00
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public reset(){
|
|
|
|
|
|
this._hits = null;
|
|
|
|
|
|
this._checkedColliders.length = 0;
|
|
|
|
|
|
this._cellHits.length = 0;
|
2020-07-31 19:33:04 +08:00
|
|
|
|
}
|
2020-06-12 08:47:13 +08:00
|
|
|
|
}
|
2020-07-23 09:10:27 +08:00
|
|
|
|
}
|