移动圆与固定线段之间的2D碰撞

Joe*_*oel 8 algorithm 2d collision-detection

在游戏程序的上下文中,我有一个移动的圆圈和一个固定的线段.该段可以具有任意大小和方向.

  • 我知道圆的半径:r
  • 我知道移动前圆圈的坐标:(xC1,yC1)
  • 我知道移动后圆圈的坐标:(xC2,yC2)
  • 我知道线段末端的坐标:(xL1,yL1) - (xL2,yL2)

移动圈

我在尝试计算时遇到困难:

  • 布尔值:如果圆的任何部分在从(xC1,yC1)移动到(xC2,yC2)的同时击中线段
  • 如果布尔值为true,则圆圈中心到达线段时的坐标(x,y)(我的意思是当圆圈第一次与切线相切时)

Gle*_*eno 6

我将用伪算法回答 - 没有任何代码.我看到它的方式有两种情况我们可能会返回true,如下图所示:

两个案例

这里的蓝色是你的圆圈,虚线是轨迹线,红线是你的给定线.

  • 我们从两个圆圈的中心构建一个辅助轨迹线.如果此轨迹线与给定线相交 - 返回true.请参阅有关如何计算该交叉点的此问题.
  • 在第二种情况下,第一次测试使我们失败,但可能恰好发生的是,无论如何,圆圈在他们传递轨迹时轻推线.我们需要以下建设: 施工

从轨迹开始,我们将法线建立到每个点A和B.然后将这些线切割或延伸到辅助线(HaHb)中,使得它们的长度AB圆正好是圆的半径.然后我们检查这些辅助线中的每一条是否与轨迹线相交.如果他们确实回归真实.

  • 否则返回false.


Mic*_*ays 3

看这里:

线段/圆交点

如果 x 或 y 计算的平方根下得到的值为负数,则该线段不相交。除此之外,您可以在获得 x 和 y 后停止计算(注意:您可能会得到两个答案)

更新我已经修改了我的答案以非常具体地解决您的问题。我将此解决方案归功于Doswa,因为我几乎是按照 C# 编写的。基本策略是我们要找到线段最接近圆心的点。基于此,我们将查看最近点的距离,如果它在半径内,则沿方向找到该点到位于圆半径处的最近点。

// I'll bet you already have one of these.
public class Vec : Tuple<double, double>
{
  public Vec(double item1, double item2) : base(item1, item2) { }
  public double Dot(Vec other) 
    { return Item1*other.Item1 + Item2*other.Item2; }
  public static Vec operator-(Vec first, Vec second) 
    { return new Vec(first.Item1 - second.Item1, first.Item2 - second.Item2);}
  public static Vec operator+(Vec first, Vec second) 
    { return new Vec(first.Item1 + second.Item1, first.Item2 + second.Item2);}
  public static Vec operator*(double first, Vec second) 
    { return new Vec(first * second.Item1, first * second.Item2);}
  public double Length() { return Math.Sqrt(Dot(this)); }
  public Vec Normalize() { return (1 / Length()) * this; }
}

public bool IntersectCircle(Vec origin, Vec lineStart, 
      Vec lineEnd, Vec circle, double radius, out Vec circleWhenHit)
{
    circleWhenHit = null;

    // find the closest point on the line segment to the center of the circle
    var line = lineEnd - lineStart;
    var lineLength = line.Length();
    var lineNorm = (1/lineLength)*line;
    var segmentToCircle = circle - lineStart;
    var closestPointOnSegment = segmentToCircle.Dot(line) / lineLength;

    // Special cases where the closest point happens to be the end points
    Vec closest;
    if (closestPointOnSegment < 0) closest = lineStart;
    else if (closestPointOnSegment > lineLength) closest = lineEnd;
    else closest = lineStart + closestPointOnSegment*lineNorm;

    // Find that distance.  If it is less than the radius, then we 
    // are within the circle
    var distanceFromClosest = circle - closest;
    var distanceFromClosestLength = distanceFromClosest.Length();
    if (distanceFromClosestLength > radius) return false;

    // So find the distance that places the intersection point right at 
    // the radius.  This is the center of the circle at the time of collision
    // and is different than the result from Doswa
    var offset = (radius - distanceFromClosestLength) *
                 ((1/distanceFromClosestLength)*distanceFromClosest);
    circleWhenHit = circle - offset;

    return true;
}
Run Code Online (Sandbox Code Playgroud)