Picking with a 2d mouse in a 3d world

Started by
3 comments, last by pancomplex 15 years, 9 months ago
Hello out there! :) My friend and I are currently developing a 3d RTS game in XNA. So far, we've got the camera and some basic stuff going. I have figured out how to make a selection bar with 2d graphics, but my problem starts when I have to figure out how to select units in the 3d world that uses floating points when the mouse spits out x and y integers. What is the most suitable way to accomplish this? Regards, Christian
Advertisement
Picking algorithms are fairly common place, albeit I don't know of any XNA specific example code.

Simply put you need to convert your 2D mouse coordinate into a 3D ray - unproject <x,y,0.0> and <x,y,1.0> into world space and you can define a line segment. You then do collision detection along this line with any pickable objects - returning on the first/closest intersection.

By the looks of thing, Viewport.Unproject() is the method you'll be wanting to use.

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

This is basically what I use for picking in my level editor:

/// <summary>/// Casts a ray and returns the closest object that the ray intersects/// (if it intersects one at all)/// </summary>/// <param name="x">Normalized screen-space X</param>/// <param name="y">Normalized screen-space Y</param>/// <returns>Returns the intersected object if successful, otherwise returns false.</returns>public GameObject ClickTest(float x, float y){    Vector4 positionSS = new Vector4(x, y, 1.0f, 1.0f);    Matrix viewProjMatrix = camera.ViewProjectionMatrix;    Matrix invViewProjMatrix = Matrix.Invert(viewProjMatrix);    Vector4 positionWS = Vector4.Transform(positionSS, invViewProjMatrix);    positionWS = positionWS / positionWS.W;    Vector3 rayWS = new Vector3(positionWS.X, positionWS.Y, positionWS.Z) - renderer.Camera.Position;    rayWS.Normalize();    float maxZ = 0;    GameObject pickedObject = null;    // Check with children first    foreach (GameObject levelNode in levelNodes)    {        foreach (GameObject gameObject in levelNode.Children)        {            CollisionBox box = new CollisionBox(gameObject.Position + gameObject.CollisionBox.min,                                                gameObject.Position + gameObject.CollisionBox.max);            float tnear, tfar;            if (box.RayIntersect(renderer.Camera.Position, rayWS, out tnear, out tfar) != -1)            {                Vector3 positionVS = Vector3.Transform(gameObject.Position, camera.ViewMatrix);                if ((positionVS.Z > minZ || pickedObject == null) && Math.Abs(positionVS.Z) < camera.FarClip)                {                    minZ = positionVS.Z;                    pickedObject = gameObject;                }            }        }    }    return pickedObject;}


As you can see it's bounding box-based...I use Fabio Policarpo's BoxCollider library to handle the intersection of a ray with a box. The math for that is relatively simple though...if you need to see how that's done you can just download the library and have a peek at the source for the CollisionBox class.
There's a sample on the CC site.

Former Microsoft XNA and Xbox MVP | Check out my blog for random ramblings on game development

How exactly does this work? What will I have to plug in to the Unproject in order to do this? And do I want to convert the mouse to 3d cordinates og the 3d cordinates og 2d cordinates?

Edit: I figured it out with the picking tutorial :) thanks a lot everybody!

This topic is closed to new replies.

Advertisement