XNA、C# - 检查 Vector2 路径是否与另一个 Vector2 路径交叉

发布于 2024-10-08 20:12:34 字数 882 浏览 1 评论 0原文

我有一个 XNA 问题要问那些在这些问题上比我更有经验的人(数学)。

背景:我有一个实现边界类的游戏,它只托管 2 个 Vector2 对象,一个起点和一个终点。当前的实现通过假设边界始终是垂直或水平来粗略地处理碰撞检测,即如果 start.x 和 end.x 是相同的检查,我不会尝试通过 x 等。

理想情况下,我想要实现的是一种接受的方法两个 Vector2 参数。第一个是当前位置,第二个是请求的位置(假设没有异议,我想将其移动到该位置)。该方法还接受一个边界对象。然后该方法应该做的是告诉我在这一步中我是否要跨越边界。这可能是一个布尔值,或者理想情况下代表我实际可以移动多远的东西。

这种空洞的方法可能比我用语言更好地解释。

        /// <summary>
    /// Checks the move.
    /// </summary>
    /// <param name="current">The current.</param>
    /// <param name="requested">The requested.</param>
    /// <param name="boundry">The boundry.</param>
    /// <returns></returns>
    public bool CheckMove(Vector2 current, Vector2 requested, Boundry boundry)
    { 
        //return a bool that indicated if the suggested move will cross the boundry.
        return true;
    }

I have an XNA question for those with more experience in these matters than myself (maths).

Background: I have a game that implements a boundary class, this simply hosts 2 Vector2 objects, a start and an end point. The current implementation crudely handles collision detection by assuming boundaries are always vertical or horizontal, i.e. if start.x and end.x are the same check I am not trying to pass x etc.

Ideally what I would like to implement is a method that accepts two Vector2 parameters. The first being a current location, the second being a requested location (where I would like to move it to assuming no objections). The method would also accept a boundary object. What the method should then do is tell me if I am going to cross the boundry in this move. this could be a bool or ideally something representing how far I can actually move.

This empty method might explain better than I can in words.

        /// <summary>
    /// Checks the move.
    /// </summary>
    /// <param name="current">The current.</param>
    /// <param name="requested">The requested.</param>
    /// <param name="boundry">The boundry.</param>
    /// <returns></returns>
    public bool CheckMove(Vector2 current, Vector2 requested, Boundry boundry)
    { 
        //return a bool that indicated if the suggested move will cross the boundry.
        return true;
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

不羁少年 2024-10-15 20:12:34

经过相当多的研究,最重要的是找到了谷歌的正确术语,我有了一个解决方案,我刚刚将其内置到我的测试台中,并且它运行得很好。

我拿了网上找到的一个例子进行了测试,然后修改了一下,以满足这个问题的需要。应该指出的是,自测试以来我已经进行了更改,因为我在这台笔记本电脑上只有一个虚拟机,无法运行 XNA 应用程序,因此虽然我确信它应该可以工作,但可能会出现错误。

我们需要做的就是传入一个表示我们所在位置的向量、一个表示我们想要的位置的向量以及边界(实际上是由起始向量和结束向量组成的线)。该方法将告诉我们两条线(一条是当前位置和请求位置之间的路径,另一条是边界)是否交叉。作为一个额外的好处,如果他们进行交叉和输出参数会告诉我们在哪里。

这是一个非常有用的系统,用于创建强大的碰撞检测。

        /// <summary>
    /// Based on the 2d line intersection method from "comp.graphics.algorithms Frequently Asked Questions"
    /// </summary>
    /// <param name="currentLocation">The current location.</param>
    /// <param name="requestedLocation">The requested location.</param>
    /// <param name="boundary">The boundary.</param>
    /// <param name="collisionVector">The collision vector.</param>
    /// <returns></returns>
    public static bool Intersects(Vector2 currentLocation, Vector2 requestedLocation, Boundary boundary, ref Vector2 collisionVector)
    {
        float q = (currentLocation.Y - boundary.Start.Y) * (boundary.End.X - boundary.Start.X) - (currentLocation.X - boundary.Start.X) * (boundary.End.Y - boundary.Start.Y);
        float d = (requestedLocation.X - currentLocation.X) * (boundary.End.Y - boundary.Start.Y) - (requestedLocation.Y - currentLocation.Y) * (boundary.End.X - boundary.Start.X);

        if (d == 0) 
        {
            return false;
        }

        float r = q / d;

        q = (currentLocation.Y - boundary.Start.Y) * (requestedLocation.X - currentLocation.X) - (currentLocation.X - boundary.Start.X) * (requestedLocation.Y - currentLocation.Y);
        float s = q / d;

        if (r < 0 || r > 1 || s < 0 || s > 1)
        {
            return false;
        }

        collisionVector.X = currentLocation.X + (int)(0.5f + r * (requestedLocation.X - currentLocation.X));
        collisionVector.Y = currentLocation.Y + (int)(0.5f + r * (requestedLocation.Y - currentLocation.Y));
        return true;
    }

After a fair bit of research and more than anything finding out the right terms to google I have a solution that I have just built into my test bed and it works perfectly.

I have taken an example found on the net and tested it, then changed it to meet the needs of this question. It should be noted I have made changes since testing this as I only have a VM on this laptop that cannot run XNA apps so while I am confident it should work there may be mistakes.

All we need to do is pass in a vector of where we are, a vector of where we would like to be and the boundary (which is effectively a line made up of a start and end vector). The method will tell tell us if the two lines (one being the path between the current location and the requested location and the other being the boundary) cross. As an added bonus if they do cross and out parameter will tell us where.

This is a very useful system for creating powerful collision detection.

        /// <summary>
    /// Based on the 2d line intersection method from "comp.graphics.algorithms Frequently Asked Questions"
    /// </summary>
    /// <param name="currentLocation">The current location.</param>
    /// <param name="requestedLocation">The requested location.</param>
    /// <param name="boundary">The boundary.</param>
    /// <param name="collisionVector">The collision vector.</param>
    /// <returns></returns>
    public static bool Intersects(Vector2 currentLocation, Vector2 requestedLocation, Boundary boundary, ref Vector2 collisionVector)
    {
        float q = (currentLocation.Y - boundary.Start.Y) * (boundary.End.X - boundary.Start.X) - (currentLocation.X - boundary.Start.X) * (boundary.End.Y - boundary.Start.Y);
        float d = (requestedLocation.X - currentLocation.X) * (boundary.End.Y - boundary.Start.Y) - (requestedLocation.Y - currentLocation.Y) * (boundary.End.X - boundary.Start.X);

        if (d == 0) 
        {
            return false;
        }

        float r = q / d;

        q = (currentLocation.Y - boundary.Start.Y) * (requestedLocation.X - currentLocation.X) - (currentLocation.X - boundary.Start.X) * (requestedLocation.Y - currentLocation.Y);
        float s = q / d;

        if (r < 0 || r > 1 || s < 0 || s > 1)
        {
            return false;
        }

        collisionVector.X = currentLocation.X + (int)(0.5f + r * (requestedLocation.X - currentLocation.X));
        collisionVector.Y = currentLocation.Y + (int)(0.5f + r * (requestedLocation.Y - currentLocation.Y));
        return true;
    }
偷得浮生 2024-10-15 20:12:34

我已经提出了一个答案,但这里有另一个答案,相似但不同:

我将使用点线距离的变体,再次取自 Wolfram! http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html

如果采用等式 14,但去掉分子中的绝对值 ( | | ),您将得到一个有符号距离。我从未听说过有符号距离的概念,所以不要指望这个术语是准确的。好的,如果出发地和目的地的符号距离不同,则意味着出发地和目的地位于线路的两侧:

public bool CheckMove(Vector2 current, Vector2 requested, Boundry boundry)
{
    // Assuming you got 2 points in boundry.
    float signedDistanceCurrent = GetSignedDistance(current, boundry.Vector1, boundry.Vector2);
    float signedDistanceRequested = GetSignedDistance(current, boundry.Vector1, boundry.Vector2);

    return signedDistanceRequested * signedDistanceCurrent < 0;
}

public float GetSignedDistance(Vector2 point0, Vector2 point1, Vector2 point2)
{
    // Equation 14 without the |s.
    return ((point2.X-point1.X)*(point1.Y-point0.Y)-(point2.X-point0.X)*(point2.Y-point1.Y))/((point2.X-point1.X)^2+(point2.Y-point1.Y)^2)^(0.5F);
}

顺便说一句,与上一个答案相比,我更喜欢这个答案,但这取决于你。

编辑:顺便说一句,用这种方法,您还可以知道碰撞何时发生:如果您这样做:

float timeFractionToCollision = -signedDistanceCurrent / (signedDistanceRequested - signedDistanceCurrent);

您将获得碰撞发生的时间段的周期分数。

I suggested an answer already, but here is another one, similar, yet different :

I would use a variant of the Point-Line Distance, as taken from Wolfram once again! http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html

If you take equation 14, but take off the absolute ( | | ) in the numerator, you get a signed distance. I never heard about a signed distance concept so don't expect this term to be accurate though. Ok, so if the signed distance of the origin and the destination are different, it means that the origin and the destination are on opposite sides of the line :

public bool CheckMove(Vector2 current, Vector2 requested, Boundry boundry)
{
    // Assuming you got 2 points in boundry.
    float signedDistanceCurrent = GetSignedDistance(current, boundry.Vector1, boundry.Vector2);
    float signedDistanceRequested = GetSignedDistance(current, boundry.Vector1, boundry.Vector2);

    return signedDistanceRequested * signedDistanceCurrent < 0;
}

public float GetSignedDistance(Vector2 point0, Vector2 point1, Vector2 point2)
{
    // Equation 14 without the |s.
    return ((point2.X-point1.X)*(point1.Y-point0.Y)-(point2.X-point0.X)*(point2.Y-point1.Y))/((point2.X-point1.X)^2+(point2.Y-point1.Y)^2)^(0.5F);
}

Btw, I prefer this answer to my previous one, but it's up to you.

Edit: Btw, with that method, you can also know when the collision happened : If you do this :

float timeFractionToCollision = -signedDistanceCurrent / (signedDistanceRequested - signedDistanceCurrent);

You'll get the fraction of the period fraction of the time period the collision happened in.

无畏 2024-10-15 20:12:34

我想你想要的是找到它们的角度,所以只需做 X/Y 并比较,如果角度相同,它们是平行的..

像这样:

float radians = (float)Math.Atan2(vector.X, vector.Y);

i think what you want is to find their angle, so just do X/Y and compare, if the angle is same, they are parallel..

like this:

float radians = (float)Math.Atan2(vector.X, vector.Y);
笑着哭最痛 2024-10-15 20:12:34

我认为你想要做的是 2D 射线盒相交(与轴对齐的盒)。您可以找到许多在线算法,包括这个:http:// www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm

I think what you're trying to do is a 2D ray-box intersection (with an axis-aligned box). You can find many online algorithms for this, including this one: http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm

过期情话 2024-10-15 20:12:34

对于您所有的数学需求,我建议您首先尝试 MathWorld.Wolfram.Com。这是一篇关于线与线相交的文章: http://mathworld.wolfram.com/Line-LineIntersection .html

您感兴趣的部分是上面的部分,包括第四个方程。

使用它,您可以获得交叉点坐标。然后您可以检查在该运动过程中是否会穿过该坐标。对于此检查,您可以执行以下操作:

public bool CheckMove(Vector2 current, Vector2 requested, Boundry boundry)
{
    // If your object doesn't move, it can't hit anything!
    if (current.X == requested.X && current.Y == requested.Y) { return false; }

    Vector2 impactPoint = /* Your code to get the impact
                            point from the link above */;

    Vector2 movement = requested - current;
    float timeToImpact = 0;
    // We checked for object movement already, so it's either moving in X, Y or both.
    // I choose to check for X first, but you could check for Y.
    if (movement.X != 0)
    {
        timeToImpact = (impactPoint.X - current.X) / movement.X;
    }
    else
    {
        timeToImpact = (impactPoint.Y - current.Y) / movement.Y;
    }

    // I don't consider a collision at 0 to be a collision since I think
    // it had to be resolved the period before.
    return timeToImpact <= 1 && timeToImpact > 0;
}

For all your mathematical needs, I suggest you first try on MathWorld.Wolfram.Com. Here is an article about Line-Line Intersection : http://mathworld.wolfram.com/Line-LineIntersection.html

The part that interests you is up, and including, the 4th equation.

Using that, you get the intersection coordinate. Then you can check if that coordinate will be crossed during that movement. For this check, you can do something like :

public bool CheckMove(Vector2 current, Vector2 requested, Boundry boundry)
{
    // If your object doesn't move, it can't hit anything!
    if (current.X == requested.X && current.Y == requested.Y) { return false; }

    Vector2 impactPoint = /* Your code to get the impact
                            point from the link above */;

    Vector2 movement = requested - current;
    float timeToImpact = 0;
    // We checked for object movement already, so it's either moving in X, Y or both.
    // I choose to check for X first, but you could check for Y.
    if (movement.X != 0)
    {
        timeToImpact = (impactPoint.X - current.X) / movement.X;
    }
    else
    {
        timeToImpact = (impactPoint.Y - current.Y) / movement.Y;
    }

    // I don't consider a collision at 0 to be a collision since I think
    // it had to be resolved the period before.
    return timeToImpact <= 1 && timeToImpact > 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文