多边形圆碰撞algorithm不起作用

编辑:更新的代码和video

我一直在研究我的碰撞检测系统的最后一个部分 – 多边形 – 圆形碰撞方法。 我几乎在那里,但有一些小问题,似乎popup与它。 我将解释我所看到的一些事情,但是看下面这个简短的video可能更容易:

https://1drv.ms/v/s!AjUDP95TjAMqje8AVA0LpPfeszVYoA

有三件事我看到:

  • 当坡度下降的时候有一点颠簸(我猜我只需要平滑一点运动调整
  • 在绕过某些角落时,调整距离得到错误计算,结果是:
    • 一点多余的距离
    • 大量的多余的距离

下面是我的代码的主要组成部分,很高兴更新post,并添加任何其他相关的代码块,使需要。 任何援助与此将不胜感激。 谢谢!

物理世界更新方法:

IEnumerable<PhysicsFixtureBase> results = this.physicsTree.Query(fixture); foreach (PhysicsFixtureBase otherFixture in results) { Vector3 adjustment = Vector3.Zero; if (fixture.Body != otherFixture.Body && fixture.Intersects(otherFixture, out adjustment)) { this.OnContact(fixture, otherFixture); body.SetVelocity(Vector3.Zero); body.MovePosition3D(adjustment); } } 

主要交叉点方法:

 public static bool Intersects(PhysicsCircleFixture circle, PhysicsPolygonFixture otherPolygon, out Vector3 adjustment) { bool result = PhysicsCollisionHandler.Intersects(otherPolygon, circle, out adjustment); adjustment *= -1; return result; } public static bool Intersects(PhysicsPolygonFixture polygon, PhysicsCircleFixture otherCircle, out Vector3 adjustment) { adjustment = Vector3.Zero; Vector2 circleCenter = otherCircle.GetPosition2D(); float circleRadius = otherCircle.GetSize2D().X; Vector2 closestPoint = Vector2.Zero; float closestDistance = float.MaxValue; foreach (Vector2 point in polygon.Points) { float distance = circleCenter.Distance(point); if (distance < closestDistance) { closestDistance = distance; closestPoint = point; } } Vector2 previousPoint = polygon.Points[polygon.Points.Count - 1]; foreach (Vector2 currentPoint in polygon.Points) { Vector2 edge = currentPoint - previousPoint; float interp = edge.DotProduct(circleCenter - previousPoint) / edge.DotProduct(edge); if (0.0f < interp && interp < 1.0f) { Vector2 point = previousPoint + edge * interp; float distance = circleCenter.Distance(point); if (distance < closestDistance) { closestDistance = distance; closestPoint = point; } } previousPoint = currentPoint; } if (closestDistance < circleRadius) { float penetrationDistance = circleRadius - closestDistance; Vector2 normalizedVector = (closestPoint - circleCenter) / closestDistance; adjustment = (normalizedVector * penetrationDistance).To3DVector3(); } return (adjustment != Vector3.Zero); } 

帮手代码:

 public static Vector2 Normal(this Vector2 vector) { return new Vector2(-vector.Y, vector.X); } public class Range { public float Min { get; set; } public float Max { get; set; } public Range(float min, float max) { this.Min = min; this.Max = max; } public float Length() { return this.Max - this.Min; } public Range Intersects(Range other) { Range longestDistance = new Range(Math.Min(this.Min, other.Min), Math.Max(this.Max, other.Max)); if (longestDistance.Length() < (this.Length() + other.Length())) { return new Range(Math.Max(this.Min, other.Min), Math.Min(this.Max, other.Max)); } return null; } } public Range PhysicsPolygonFixture.ProjectOnAxis(Vector2 axis) { Range range = new Range(float.MaxValue, float.MinValue); foreach (Vector2 point in this.Points) { float projection = point.DotProduct(axis); if (projection < range.Min) { range.Min = projection; } if (projection > range.Max) { range.Max = projection; } } return range; } public Range PhysicsCircleFixture.ProjectOnAxis(Vector2 axis) { float circleCenterProjection = this.GetPosition2D().DotProduct(axis); float circleRadius = this.GetSize2D().X; return new Range(circleCenterProjection - circleRadius, circleCenterProjection + circleRadius); } 

检查边缘碰撞的代码肯定会导致您遇到的问题。 既然它比必要的复杂一点,我认为正确的解决办法是完全重写它。

删除整个foreach (Vector2 edge in polygon.Edges)循环,并在该行上方添加以下代码if (closestDistance < circleRadius)

 Vector2 pointPrev = polygon.Points[polygon.Points.Length - 1]; foreach (Vector2 pointCur in polygon.Points) { Vector2 edge = pointCur - pointPrev; float interp = edge.DotProduct(circleCenter - pointPrev) / edge.DotProduct(edge); if (interp > 0.0f && interp < 1.0f) { Vector2 point = pointPrev + edge * interp; float distance = circleCenter.Distance(point); if (distance < closestDistance) { closestDistance = distance; closestPoint = point; } } pointPrev = pointCur; } 

注1

我试图使用你的函数和types名称。 唯一没有引用的是polygon.Points是一个数组还是一个列表。

笔记2

唯一棘手的是计算interp ; 它将两个计算结合成一个来避免平方根。 它将边缘向量归一化,并将边缘向量的长度将圆心的投影分割到边缘上。