2
votes

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;
    }
5
Is this "boundary" a rectangle, line, line segment, or something else?John McDonald
Hi John, The boundary is a line (ops! just noticed my awful spelling). For clarification the simple way of solving this problem would be to store every Vector2 that was a boundary in a collection then as an object is moving check said collection to make sure we have not hit a Vector2 in that collection. This is however a real pain for level development and pretty expensive on the old processing time. Hence since pretty much every boundary is a line it made sense to only store the start and end vector2 objects and mathematically calculate on the fly if it has been hit/crossed. Hope this helpsNick

5 Answers

2
votes

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.

2
votes

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;
    }
0
votes

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);
0
votes

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

0
votes

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;
}