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

时间:2011-08-14 23:44:21

标签: algorithm 2d collision-detection

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

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

moving circle

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

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

3 个答案:

答案 0 :(得分:6)

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

Two cases

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

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

从轨迹中我们为每个点A和B构建法线。然后将这些线切割或扩展为辅助线(HaHb),使其长度为{{1} }和A正好是圆的半径。然后我们检查这些辅助线中的每一条是否与轨迹线相交。如果他们返回true。

  • 否则返回false

答案 1 :(得分:3)

看这里:

Line segment / Circle intersection

如果在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;
}

答案 2 :(得分:1)

这是一些计算从点到线的距离的Java(这不完整,但会给你基本的图片)。代码来自一个名为的类 '向量'。假设是矢量对象被初始化为线矢量。方法'distance'接受线矢量开始的点(当然称为'at')和感兴趣的点。它计算并返回从该点到该线的距离。

public class Vector
{
double x_ = 0;
double y_ = 0;
double magnitude_ = 1;

public Vector()
{
}

public Vector(double x,double y)
{
    x_ = x;
    y_ = y;
}

public Vector(Vector other)
{
    x_ = other.x_;
    y_ = other.y_;
}

public void add(Vector other)
{
    x_ += other.x_;
    y_ += other.y_;
}

public void scale(double val)
{
    x_ *= val;
    y_ *= val;
}

public double dot(Vector other)
{
    return x_*other.x_+y_*other.y_;
}

public void cross(Vector other)
{
    x_ = x_*other.y_ - y_*other.x_;
}

public void unit()
{
    magnitude_ = Math.sqrt(x_*x_+y_*y_);
    x_/=magnitude_;
    y_/=magnitude_;
}

public double distance(Vector at,Vector point)
{
    //
    // Create a perpendicular vector
    //
    Vector perp = new Vector();
    perp.perpendicular(this);
    perp.unit();

    Vector offset = new Vector(point.x_ - at.x_,point.y_ - at.y_);
    double d = Math.abs(offset.dot(perp));

    double m = magnitude();
    double t = dot(offset)/(m*m);
    if(t < 0)
    {
        offset.x_ -= at.x_;
        offset.y_ -= at.y_;
        d = offset.magnitude();
    }
    if(t > 1)
    {
        offset.x_ -= at.x_+x_;
        offset.y_ -= at.y_+y_;
        d = offset.magnitude();
    }
    return d;
}

private void perpendicular(Vector other)
{
    x_ = -other.y_;
    y_ = other.x_;
}

public double magnitude()
{
    magnitude_ = Math.sqrt(x_*x_+y_*y_);
    return magnitude_;
}
}