Algorithm 移动圆和固定线段之间的二维碰撞

Algorithm 移动圆和固定线段之间的二维碰撞,algorithm,2d,collision-detection,Algorithm,2d,Collision Detection,在游戏程序的上下文中,我有一个移动的圆和一个固定的线段。线段可以具有任意大小和方向 我知道圆的半径:r 我知道移动前圆的坐标:(xC1,yC1) 我知道移动后圆的坐标:(xC2,yC2) 我知道线段端点的坐标:(xL1,yL1)-(xL2,yL2) 我在计算时遇到困难: 布尔值:如果圆的任何部分在从(xC1,yC1)移动到(xC2,yC2)时碰到线段 如果布尔值为真,则圆与线段相切时圆中心的坐标(x,y) 看这里: 如果计算x或y的平方根下的值为负值,则线段不相交。除此之外,你可以在得

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

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

我在计算时遇到困难:

  • 布尔值:如果圆的任何部分在从(xC1,yC1)移动到(xC2,yC2)时碰到线段
  • 如果布尔值为真,则圆与线段相切时圆中心的坐标(x,y)
看这里:

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

更新我已经修改了我的答案,以非常明确地解决您的问题。我对这个解决方案表示赞赏,因为我几乎遵循了这个方法,并为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;
}
//我敢打赌你已经有一个了。
公共类向量:元组
{
公共Vec(双项1,双项2):基(项1,项2){
公共双点(Vec其他)
{返回Item1*other.Item1+Item2*other.Item2;}
公共静态Vec运算符-(Vec第一,Vec第二)
{返回新的Vec(first.Item1-second.Item1,first.Item2-second.Item2);}
公共静态Vec运算符+(Vec第一,Vec第二)
{返回新的Vec(first.Item1+second.Item1,first.Item2+second.Item2);}
公共静态Vec运算符*(双精度优先,Vec秒)
{返回新的Vec(first*second.Item1,first*second.Item2);}
public double Length(){return Math.Sqrt(Dot(this));}
public Vec Normalize(){return(1/Length())*this;}
}
公共布尔交叉圆(矢量原点、矢量测线起点、,
矢量线端点、矢量圆、双半径、外矢量圆(HENHIT)
{
circleWhenHit=null;
//在线段上找到离圆心最近的点
var line=lineEnd-lineStart;
var lineLength=line.Length();
var lineNorm=(1/行长)*行;
var segmentToCircle=圆-线路起点;
var closestPointOnSegment=段到圆点(线)/线宽;
//最近点恰好是终点的特殊情况
Vec最近;
如果(ClosesPointOnSegment<0)最近=线路开始;
否则,如果(closestPointOnSegment>lineLength)最近=lineEnd;
else closest=线路起点+闭合点分段*线路标准;
//找到那个距离。如果它小于半径,那么我们
//你在圈内吗
var distanceFromClosest=圆-最近;
var distance fromClosestLength=距离fromClosest.Length();
if(distance fromClosestLength>radius)返回false;
//因此,求出交点所在的距离
//半径。这是碰撞时圆的中心
//与Doswa的结果不同
变量偏移=(半径-距离闭合长度)*
((1/距离最短长度)*距离最近长度);
circleWhenHit=圆-偏移;
返回true;
}

我将用伪算法来回答,而不需要任何代码。在我看来,有两种情况下我们可能会返回true,如下图所示:

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

  • 我们建立了一条辅助轨迹线,从和到两个圆的中心。如果此轨迹线与给定线相交-返回true。有关如何计算该交点的信息,请参见
  • 在第二种情况下,第一次测试失败了,但不管怎样,当圆圈在轨道上通过时,它们可能会轻推直线。我们需要进行以下施工:
从轨迹出发,我们建立到每个点A和B的法线。然后这些线被截断或延伸成辅助线(
Ha
Hb
),这样它们从
A
B
开始的长度正好是圆的半径。然后我们检查这些辅助线是否与轨迹线相交。如果是,则返回true。

  • 否则返回false

这里有一些Java计算点到线的距离(这还不完整,但会给出基本的图片)。代码来自一个名为 “向量”。假设向量对象初始化为线向量。“距离”方法接受线向量开始的点(当然称为“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_;
}
}
公共类向量
{
双x=0;
双y=0;
双幅_uu1;
公共向量()
{
}
公共向量(双x,双y)
{
x_uux=x;
y=y;
}
公共媒介(其他媒介)
{
x_uu=其他.x_uu;
y_uu=其他。y_uu;
}
公共无效添加(矢量其他)
{
x_u+=其他.x_3;;
y_u+=其他y_u;
}
公共空隙率(双val)
{
x_*=val;
y*=val;
}
公共双点(矢量其他)
{
返回x*other.x+y*other.y;
}
公共空间交叉(矢量其他)
{
x_ux=x_*other.y_u-y_*other.x;
}
公共voi