1
\$\begingroup\$

I am attempting to find the furthermost point in my game world given the player's current location and a normalized direction vector in screen space. My current algorithm is:

  1. convert player world location to screen space
  2. multiply the direction vector by a large number (2000) and add it to the player's screen location to get the distant screen location
  3. convert the distant screen location to world space
  4. create a line running from the player's world location to the distant world location
  5. loop over the bounding "walls" (of which there are always 4) of my game world
    1. check whether the wall and the line intersect
      1. if so, where they intersect is the furthermost point of my game world in the direction of the vector

Here it is, more or less, in code:

public Vector2 GetFurthermostWorldPoint(Vector2 directionVector)
{
    var screenLocation = entity.WorldPointToScreen(entity.Location);
    var distantScreenLocation = screenLocation + (directionVector * 2000);
    var distantWorldLocation = entity.ScreenPointToWorld(distantScreenLocation);
    var line = new Line(entity.Center, distantWorldLocation);
    float intersectionDistance;
    Vector2 intersectionPoint;

    foreach (var boundingWall in entity.Level.BoundingWalls)
    {
        if (boundingWall.Intersects(line, out intersectionDistance, out intersectionPoint))
        {
            return intersectionPoint;            
        }
    }

    Debug.Assert(false, "No intersection found!");
    return Vector2.Zero;
}

Now this works, for some definition of "works". I've found that the further out my distant screen location is, the less chance it has of working. When digging into the reasons why, I noticed that calls to Viewport.Unproject could result in wildly varying return values for points that are "far away". I wrote this stupid little "test" to try and understand what was going on:

[Fact]
public void wtf()
{
    var screenPositions = new Vector2[]
        {
            new Vector2(400, 240),
            new Vector2(400, -2000),
        };

    var viewport = new Viewport(0, 0, 800, 480);
    var projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, viewport.Width / viewport.Height, 1, 200000);
    var viewMatrix = Matrix.CreateLookAt(new Vector3(400, 630, 600), new Vector3(400, 345, 0), new Vector3(0, 0, 1));
    var worldMatrix = Matrix.Identity;

    foreach (var screenPosition in screenPositions)
    {
        var nearPoint = viewport.Unproject(new Vector3(screenPosition, 0), projectionMatrix, viewMatrix, worldMatrix);
        var farPoint = viewport.Unproject(new Vector3(screenPosition, 1), projectionMatrix, viewMatrix, worldMatrix);

        Console.WriteLine("For screen position {0}:", screenPosition);
        Console.WriteLine("   Projected Near Point = {0}", nearPoint.TruncateZ());
        Console.WriteLine("   Projected Far Point = {0}", farPoint.TruncateZ());
        Console.WriteLine();
    }
}

The output I get on the console is:

  For screen position {X:400 Y:240}:
     Projected Near Point = {X:400 Y:629.571 Z:599.0967}
     Projected Far Point = {X:392.9302 Y:-83074.98 Z:-175627.9}

  For screen position {X:400 Y:-2000}:
     Projected Near Point = {X:400 Y:626.079 Z:600.7554}
     Projected Far Point = {X:390.2068 Y:-767438.6 Z:148564.2}

My question is really twofold:

  1. what am I doing wrong with the unprojection such that it varies so wildly and, thus, does not allow me to determine the corresponding world point for my distant screen point?
  2. is there a better way altogether to determine the furthermost point in world space given a current world space location, and a directional vector in screen space?
\$\endgroup\$
1
  • \$\begingroup\$ PS. I used such a high value for the far plane distance (200000) because it seemed to give me a better chance of being able to unproject. I have no idea why - I can't find any documentation on what different near/far plane distances will actually achieve. Any direction on that would be appreciated too. \$\endgroup\$
    – me--
    Commented Mar 28, 2012 at 15:51

1 Answer 1

1
\$\begingroup\$

Perhaps first find the line intersection in world coordinates, and then convert that point to screen coordinates?

\$\endgroup\$
4
  • \$\begingroup\$ Thought of that, but the input is a gesture which is thus in screen space. It would be weird to use that gesture in world space rather than screen, hence the order I do things. \$\endgroup\$
    – me--
    Commented Mar 28, 2012 at 16:58
  • \$\begingroup\$ like this? i2.photobucket.com/albums/y39/karmis/mockups/edgeofworld.png \$\endgroup\$
    – karmington
    Commented Mar 28, 2012 at 20:58
  • \$\begingroup\$ If I understand correctly you could take a point between the red and green dots in screen space, making sure it is on top of the map, move that point to world, use that direction to get the world intersect, and then back? Even if it's an arbitrarily drawn line, not centered from character when drawn, you can take that line and center it, and use that. Granted this is not throwing light on why the original idea does not work. Not sure I understand why you want a screen position so far out offscreen? \$\endgroup\$
    – karmington
    Commented Mar 28, 2012 at 21:12
  • \$\begingroup\$ Yes, I am now doing it as per your comment and it works - thanks. Only "problem" is it takes one extra step in the calculation, so is slightly slower. The reason for projecting so far out is to be 100% sure that the line does indeed cross one of the bounding lines of my level. \$\endgroup\$
    – me--
    Commented Mar 29, 2012 at 17:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .