My current game project involved the use of selection of object (picking) in 3d world.
So I check out the XNA Creator Club Forum for such resource and found one, Picking Example.
But what I will implement right now is not the same as the sample, but I will extend it a little by only show the closest object to us. In this case, it means that we apply the ‘One selection mode‘ not ‘Multiple selection mode’ like in the xna sample.
Note that also, I will rewrite all the source in my own way which I found it’s very neat :)
Ok, you now know what I’m trying to say. So let’s implement it right away.
But first I want to show you some idea on this demo program.
Looking at the image below you will see that only one object’s name will be show.
Implemenation
Let’s turn to our implementation.
I will cover only the most important part, you should go along by your own after reading this.
In order to select the object in 3d space with mouse cursor, we need to transform our cursor’s position which is 2d space into 3d space. I use ray-casting, which is just the unit vector of direction begin from z-depth equal to 0 and end at the farthest point (but remember it’s only unit vector so the farthest point is 1).
By create the ray, we can calculate the intersection between object’s boundingsphere and our ray with ease. Thus the result is ‘picking’.
To tramsform the position in 2d space into 3d space, we use
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | //Create ray vector private Ray CreateRay() { //Convert the cursor's 2d-position to 3d position Vector2 originalCursor2D = new Vector2(mouseManager.X, mouseManager.Y); //Create cursor3d for closest and farthest //0 means closest //1 means farthest Vector3 cursor3D_near = new Vector3(originalCursor2D, 0f); Vector3 cursor3D_far = new Vector3(originalCursor2D, 1f); //transform each cursor3d-vector Vector3 transformed_cursor3D_near = device.Viewport.Unproject(cursor3D_near, projection, view, Matrix.Identity); Vector3 transformed_cursor3D_far = device.Viewport.Unproject(cursor3D_far, projection, view, Matrix.Identity); //create the direction of the ray Vector3 direction = transformed_cursor3D_far - transformed_cursor3D_near; direction.Normalize(); //return new ray by using the near point as the source return new Ray(transformed_cursor3D_near, direction); } |
Specify the identity matrix for both point.
Now we get our ray.
Before we go down talk about intersection test with object, I want to tell you about another idea to help us out.
Another to help us out is this, I will use 2 lists.
|_ First, it contains all objects in the world.
|_ Second, it contains only those that passed the intersect test with the ray.
You can found the declaration at very top of the code.
1 2 | List objectsInWorldList; List inSightList; |
Both lists will reference to the objects in our world. For the second list, when it add any new GameObject, it will perform loop check first for the position to insert the new data. This reason is that when you want to draw only the one of the selected object’s name, you will specify the index = 0, and the our job is a lot easier.
It’s nothing special about it, just contain enough information for object to be update, draw into the screen.
And as you can see, this class doesn’t perform any error-checking, it just play as simple as possible to ease the process of coding.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | class GameObject { public Model Model; public Matrix[] AbsoluteBoneTransforms; public Vector3 Position; public float Scale; public float RotationX; public float RotationY; public float RotationZ; public List Textures; public Matrix World; public String Name; public Object Tag; //Whether this game object is textured or not public bool Textured { get { if (Textures.Count == 0) return false; else return true; } } public GameObject(String name) { Position = new Vector3(0f); Textures = new List(); Scale = 1f; RotationX = 0f; RotationY = 0f; RotationZ = 0f; Name = name; } // Copy Absolute transforms into array public void CopyAbsoluteTransformsToArray() { AbsoluteBoneTransforms = new Matrix[Model.Bones.Count]; Model.CopyAbsoluteBoneTransformsTo(AbsoluteBoneTransforms); } public void Update(GameTime gameTime) { //calculate the world matrix World = Matrix.CreateScale(Scale) * Matrix.CreateRotationX(RotationX) * Matrix.CreateRotationY(RotationY) * Matrix.CreateRotationZ(RotationZ) * Matrix.CreateTranslation(Position); } public void Draw(GameTime gameTime, Matrix view, Matrix projection, bool isTextured) { if (isTextured) XNAUtil.DrawModel_defaultLight(ref Model, view, projection, World, ref Textures); else XNAUtil.DrawModel_defaultLight(ref Model, view, projection, World); } } Now only stuff you left is the checking of intersection between object and ray. See below. <pre lang="csharp" line="1"> //Coarse collision checking between object and ray public static bool CoarseCollisionObject_with_Ray(Model model, Matrix world, Ray ray) { //get boundingsphere and transform it BoundingSphere sphere = (BoundingSphere)model.Tag; sphere = XNAUtil.TransformBoundingSphere(sphere, world); //Finer collision checking between object and ray public static bool FinerCollisionObject_with_Ray(Model model, Matrix world, Ray ray, ref float? distance) { if (!CoarseCollisionObject_with_Ray(model, world, ray)) return false; Matrix[] modelTransforms = new Matrix[model.Bones.Count]; model.CopyAbsoluteBoneTransformsTo(modelTransforms); foreach (ModelMesh mesh in model.Meshes) { //transfrom the relative sphere from the big coverred the model BoundingSphere sphere = (BoundingSphere)mesh.BoundingSphere; Matrix transform = modelTransforms[mesh.ParentBone.Index] * world; sphere = XNAUtil.TransformBoundingSphere(sphere, transform); //check collision with ray if ( (distance = sphere.Intersects(ray)) != null) return true; } return false; } |
The answer to this is ‘we just want to lower the amount of calculation’, so we roughly check first then it closes enough we then perform the finer check. We have no need to perform finer check all the time.And that’s it, you are finished now :)



Классно всё: и картинка ,и информация
cooooolest domain name)))
————————
ads: http://pedeno.ru/
great domain name for blog like this)))
————————
sponsor: http://werato.ru/
Thanks everyone, I hope I can furthur contribute more on these programming articles.
Thanks again.
great domain name for blog like this)))
————————
ad: http://car-auto-loan.xetisa.ru/
now in my rss reader)))
————————
ad: http://joneri.ru/
Спасибо за статью.. Актуально мне сейчас.. Взяла себе еще перечитать.
Отличный пост, прочитав несколько статей на эту тему понял, что всё таки не посмотрел с другой стороны, а пост как-то очень заинтересовал.
а вот вопросик можно? У вас время после поста указано. Это московское? Заранее спасибо!
Ага, теперь понятно…А то я сразу не очень то и не понял где тут связь с самим заголовком…
Thanks, for you all, at least if I can traslate russia into english correctly.
I pretty much surprise that the people from russia reading my posts, anyway it’s great that these content can help you out in some way, thanks for you all again.
Добавил в свои закладки. Теперь буду вас намного почаще читать!
Очень интересно!!! Только не очень могу понять как часто обновляется ваш блог?
Ценные рекомендации, беру на заметку
очень занимательно было почитать
Очень интересно, но все в будущем хотелось бы еще побольше узнать об этом. Очень понравилась ваша статья!
Благодарю!!!У Вас часто появляются очень интересные посты! Очень поднимаете мое настроение.
Ваш пост навел меня на думки *ушел много думать* …
Отличный пост, прочитав несколько статей на эту тему понял, что всё таки не посмотрел с другой стороны, а пост как-то очень заинтересовал.
как сказал один очень умный человек которого мы все хорошо знаем )
Растить в душе побег унынья – преступленье,
Пока не прочтена вся книга наслажденья
Лови же радости и жадно пей вино:
Жизнь коротка, увы! Летят её мгновенья.
Отличный пост, прочитав несколько статей на эту тему понял, что всё таки не посмотрел с другой стороны, а пост как-то очень заинтересовал.
Можно и подискутировать по этому поводу …
А у вас пара ошибок в тексте… тока не обижайтесь )
Can you point me to that point, what’s the actual mistake.
Thanks.
I’m sorry but I can’t translate your russian language into english.
What’s the possible thing you said? (I’m so sorry about this.)
Я в принципе, мало, что смыслю в этм посте, но постараюсь все таки понять.
Спасибо вам за сайт, очень полезный ресурс, мне очень нравится
The style of writing is quite familiar . Did you write guest posts for other bloggers?
Ты как обычно радуешь нас своими лучшими фразами спасибо, беру!
First of all, thank you so much for coming by.
Yes, I have came across many blog sites.
Also I move once from my old host, so you may found some of my old posts that similar to the content of this site.
Note that the “Picking” post you read, I learned the original from the XNA Creator Club, I extended some features beyond the original one (stated in the readme file in the file downloaded from XNA Creator Club) which is “Checking for the closet object selected”. Thus it’s quite similar too.
Anyway, thanks for your visiting.
:)
Огромное вам человеческое спасибо, очень актуальная заметка.
Спасибо за статью.. Актуально мне сейчас.. Взяла себе еще перечитать.
Отличный пост, прочитав несколько статей на эту тему понял, что всё таки не посмотрел с другой стороны, а пост как-то очень заинтересовал.
Очень интересно!!! Только не очень могу понять как часто обновляется ваш блог?
Хорошо пишете. Надеюсь, когда-нибудь увижу нечто подобное и на своем блоге…
газ и уголь в России
Наверное, 1й увиденный мной блог, где в комментариях вышли спама