Picking with a 2d mouse in a 3d world
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
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
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
This is basically what I use for picking in my level editor:
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.
/// <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.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement