射线检测完善
This commit is contained in:
@@ -219,6 +219,13 @@ module es {
|
||||
return Math.sqrt((this.x * this.x) + (this.y * this.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回其长度的平方
|
||||
*/
|
||||
public lengthSquared(): number {
|
||||
return (this.x * this.x) + (this.y * this.y);
|
||||
}
|
||||
|
||||
/** 对x和y值四舍五入 */
|
||||
public round(): Vector2 {
|
||||
return new Vector2(Math.round(this.x), Math.round(this.y));
|
||||
|
||||
@@ -9,6 +9,10 @@ module es {
|
||||
* raycast是否检测配置为触发器的碰撞器
|
||||
*/
|
||||
public static raycastsHitTriggers: boolean = false;
|
||||
/**
|
||||
* 在碰撞器中开始的射线/直线是否强制转换检测到那些碰撞器
|
||||
*/
|
||||
public static raycastsStartInColliders = false;
|
||||
|
||||
public static reset() {
|
||||
this._spatialHash = new SpatialHash(this.spatialHashCellSize);
|
||||
|
||||
@@ -45,6 +45,13 @@ module es {
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
public setValuesNonCollider(fraction: number, distance: number, point: Vector2, normal: Vector2){
|
||||
this.fraction = fraction;
|
||||
this.distance = distance;
|
||||
this.point = point;
|
||||
this.normal = normal;
|
||||
}
|
||||
|
||||
public reset(){
|
||||
this.collider = null;
|
||||
this.fraction = this.distance = 0;
|
||||
|
||||
@@ -63,6 +63,18 @@ module es {
|
||||
throw new Error(`Collisions of Circle to ${other} are not supported`);
|
||||
}
|
||||
|
||||
public collidesWithLine(start: es.Vector2, end: es.Vector2, hit: es.RaycastHit): boolean {
|
||||
return ShapeCollisions.lineToCircle(start, end, this, hit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所提供的点是否在此范围内
|
||||
* @param point
|
||||
*/
|
||||
public containsPoint(point: es.Vector2) {
|
||||
return (Vector2.subtract(point, this.position)).lengthSquared() <= this.radius * this.radius;
|
||||
}
|
||||
|
||||
public pointCollidesWithShape(point: Vector2, result: CollisionResult): boolean {
|
||||
return ShapeCollisions.pointToCircle(point, this, result);
|
||||
}
|
||||
|
||||
@@ -291,6 +291,10 @@ module es {
|
||||
throw new Error(`overlaps of Polygon to ${other} are not supported`);
|
||||
}
|
||||
|
||||
public collidesWithLine(start: es.Vector2, end: es.Vector2, hit: es.RaycastHit): boolean {
|
||||
return ShapeCollisions.lineToPoly(start, end, this, hit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 本质上,这个算法所做的就是从一个点发射一条射线。
|
||||
* 如果它与奇数条多边形边相交,我们就知道它在多边形内部。
|
||||
|
||||
@@ -21,6 +21,10 @@ module es {
|
||||
|
||||
public abstract collidesWithShape(other: Shape, collisionResult: CollisionResult): boolean;
|
||||
|
||||
public abstract collidesWithLine(start: Vector2, end: Vector2, hit: RaycastHit): boolean;
|
||||
|
||||
public abstract containsPoint(point: Vector2);
|
||||
|
||||
public abstract pointCollidesWithShape(point: Vector2, result: CollisionResult): boolean;
|
||||
|
||||
public clone(): Shape {
|
||||
|
||||
@@ -312,6 +312,99 @@ module es {
|
||||
return new Rectangle(topLeft.x, topLeft.y, fullSize.x, fullSize.y)
|
||||
}
|
||||
|
||||
public static lineToPoly(start: Vector2, end: Vector2, polygon: Polygon, hit: RaycastHit): boolean {
|
||||
let normal = Vector2.zero;
|
||||
let intersectionPoint = Vector2.zero;
|
||||
let fraction = Number.MAX_VALUE;
|
||||
let hasIntersection = false;
|
||||
|
||||
for (let j = polygon.points.length - 1, i = 0; i < polygon.points.length; j = i, i ++){
|
||||
let edge1 = Vector2.add(polygon.position, polygon.points[j]);
|
||||
let edge2 = Vector2.add(polygon.position, polygon.points[i]);
|
||||
let intersection: Vector2 = Vector2.zero;
|
||||
if (this.lineToLine(edge1, edge2, start, end, intersection)){
|
||||
hasIntersection = true;
|
||||
|
||||
// TODO: 这是得到分数的正确和最有效的方法吗?
|
||||
// 先检查x分数。如果是NaN,就用y代替
|
||||
let distanceFraction = (intersection.x - start.x) / (end.x - start.x);
|
||||
if (Number.isNaN(distanceFraction) || Number.isFinite(distanceFraction))
|
||||
distanceFraction = (intersection.y - start.y) / (end.y - start.y);
|
||||
|
||||
if (distanceFraction < fraction){
|
||||
let edge = Vector2.subtract(edge2, edge1);
|
||||
normal = new Vector2(edge.y, -edge.x);
|
||||
fraction = distanceFraction;
|
||||
intersectionPoint = intersection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasIntersection){
|
||||
normal = normal.normalize();
|
||||
let distance = Vector2.distance(start, intersectionPoint);
|
||||
hit.setValuesNonCollider(fraction, distance, intersectionPoint, normal);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static lineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2, intersection: Vector2){
|
||||
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;
|
||||
|
||||
intersection = intersection.add(a1).add(Vector2.multiply(new Vector2(t), b));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static lineToCircle(start: Vector2, end: Vector2, s: Circle, hit: RaycastHit): boolean{
|
||||
// 计算这里的长度并分别对d进行标准化,因为如果我们命中了我们需要它来得到分数
|
||||
let lineLength = Vector2.distance(start, end);
|
||||
let d = Vector2.divide(Vector2.subtract(end, start), new Vector2(lineLength));
|
||||
let m = Vector2.subtract(start, s.position);
|
||||
let b = Vector2.dot(m, d);
|
||||
let c = Vector2.dot(m, m) - s.radius * s.radius;
|
||||
|
||||
// 如果r的原点在s之外,(c>0)和r指向s (b>0) 则返回
|
||||
if (c > 0 && b > 0)
|
||||
return false;
|
||||
|
||||
let discr = b * b - c;
|
||||
// 线不在圆圈上
|
||||
if (discr < 0)
|
||||
return false;
|
||||
|
||||
// 射线相交圆
|
||||
hit.fraction = -b - Math.sqrt(discr);
|
||||
|
||||
// 如果分数为负数,射线从圈内开始,
|
||||
if (hit.fraction < 0)
|
||||
hit.fraction = 0;
|
||||
|
||||
hit.point = Vector2.add(start, Vector2.multiply(new Vector2(hit.fraction), d));
|
||||
hit.distance = Vector2.distance(start, hit.point);
|
||||
hit.normal = Vector2.normalize(Vector2.subtract(hit.point, s.position));
|
||||
hit.fraction = hit.distance / lineLength;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用second检查被deltaMovement移动的框的结果
|
||||
* @param first
|
||||
|
||||
@@ -274,7 +274,7 @@ module es {
|
||||
public _ray: Ray2D;
|
||||
public _layerMask: number;
|
||||
|
||||
public start(ray: Ray2D, hits: RaycastHit[], layerMask: number){
|
||||
public start(ray: Ray2D, hits: RaycastHit[], layerMask: number) {
|
||||
this._ray = ray;
|
||||
this._hits = hits;
|
||||
this._layerMask = layerMask;
|
||||
@@ -287,9 +287,9 @@ module es {
|
||||
* @param cellY
|
||||
* @param cell
|
||||
*/
|
||||
public checkRayIntersection(cellX: number, cellY: number, cell: Collider[]): boolean{
|
||||
let fraction: number;
|
||||
for (let i = 0; i < cell.length; i ++) {
|
||||
public checkRayIntersection(cellX: number, cellY: number, cell: Collider[]): boolean {
|
||||
let fraction: number = 0;
|
||||
for (let i = 0; i < cell.length; i++) {
|
||||
let potential = cell[i];
|
||||
|
||||
// 管理我们已经处理过的碰撞器
|
||||
@@ -309,8 +309,42 @@ module es {
|
||||
// TODO: 如果边界检查返回更多数据,我们就不需要为BoxCollider检查做任何事情
|
||||
// 在做形状测试之前先做一个边界检查
|
||||
let colliderBounds = potential.bounds;
|
||||
if (colliderBounds.)
|
||||
let fraction = colliderBounds.rayIntersects(this._ray);
|
||||
if (fraction <= 1) {
|
||||
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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public reset(){
|
||||
this._hits = null;
|
||||
this._checkedColliders.length = 0;
|
||||
this._cellHits.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user