Saturday, March 16, 2013

Task #3: Object selection and highlight in Unity3d

Practical use of Unity3d engine.



Task #3: Objects selection and highlight in Unity3d

Contents

  • Task #3: Objects selection and highlight in Unity3d.
  • 1. Object selection
  • 2. Object highlight
  • Remarks


Task #3: Objects selection and highlight in Unity3d.

  This article will show solution for the task: application should implement object selection by left mouse button down. Also application should highlight selected object. This article is base on the second one (http://denis-potapenko.blogspot.com/2013/03/task-2-rendering-hotspots-in-unity3d.html). So, I suggest reading the second article first. Figure B shows result we want to achieve:
Figure В. – Result scene

Object selection

  At first, create class variable, which will store the selected object.
// selected GameObject
private GameObject mSelectedObject;

  Also, let’s create property for this object.
/// 
/// Gets or sets selected GameObject
/// 
public GameObject SelectedObject
{
    get
    {
        return mSelectedObject;
    }
    set
    {
        ...

        // assign new game object
        mSelectedObject = value;

        ...
    }
}
  Full implementation of this property will be described below.
  And now, create method, which implementation is object selection. We need to determine what object is located under the mouse. Object selection pipeline is:

  • Convert mouse position from screen space to three-dimensional space (A);
  • Build ray, directed from mouse position to “camera forward” way (B);
  • Find intersection of ray and any object in the scene;
  • Find the first object that occurred on intersection; 

  Convert mouse position to ray that spreads to “camera forward” (Combine A and B) using «ScreenPointToRay» method.
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  Now, let’s find ray intersection with any object in scene. To implement this call function «Physics.Raycast».

RaycastHit hit;
if (Physics.Raycast(ray, out hit, Constants.cMaxRayCastDistance))

  I should notice, that each object we want to get intersection should have «Collider» component. Colliders mainly used in physics, particularly to determine object intersection. To assign «Collider» to GameObject you need to select the object in the scene and choose menu item «Component / Physics / Box Collider». In future articles we will have more detailed description of this component.
  So, if intersection exist, function «Physics.Raycast» returns «true» and sets «hit» variable. Now, let’s determine intersected GameObject. 
// get game object
GameObject rayCastedGO = hit.collider.gameObject;

  And finally, set this object as selected.
// select object
this.SelectedObject = rayCastedGO;
  And now, we need to call function “SelectObjectByMousePos” in «Update» one. But we will call it only when left mouse button is pressed.
// process object selection
if (Input.GetMouseButtonDown(0))
{
    SelectObjectByMousePos();
}
  And now, let’s take a look to «SelectedObject» function implementation.

Object highlight

  At first, I’ll tell a bit of theory. Object highlight we can do by a lot of ways. I will describe a way that we can achieve without any add-ons to Unity3d engine. But after that, I will describe you a better way of highlight implementation, but it will require doing some special actions. 
  So highlight implementation is divided to such parts:
  • Create two materials: the first one is simple material, and the second material is used for highlight;
  • Pass these materials to script;
  • When object is selected set its material to highlighted;
  • To object that was selected before, set simple material;
  Now, let’s take a look to implementation. In Unity3d editor create folder with materials and name it «Materials». In this folder create two materials: simple “SimpleMat” and highlighted “HighlightedMat”. Result folder tree is shown on figure 2.1.

Figure 2.1 – Files and folders structure with created materials

  I want to notice that “Diffuse” shader is set to both materials. Materials are configured identically, except that the material «HighlightedMat» has diffuse color set to red. Material settings of «HighlightedMat» are shown on figure 2.2.
Figure 2.2 – Settings of «HighlightedMat» material

  Now, create two public class variables in “AppRoot” class, which will contain materials data.
// materials for highlight
public Material SimpleMat;
public Material HighlightedMat;
  Set value to this variables. We can do it directly in the editor. Process of assigning values to materials is shown on figure 2.3.
Figure 2.3 – Process of assigning materials to the script "AppRoot" 


  So, finally let’s have a look to implementation of the setter of the property “SelectedObject”. 
Get «old» selected object.

// get old game object
GameObject goOld = mSelectedObject;
  Set new selected object.
// assign new game object
mSelectedObject = value;
  If selected object is not changed, just leave this function.
// if this object is the same - just not process this
if (goOld == mSelectedObject)
{
    return;
}
  Set material to old selected object.
// set material to non-selected object
if (goOld != null)
{
    goOld.renderer.material = SimpleMat;
}
  Set material to selected object.
// set material to selected object
if (mSelectedObject != null)
{
    mSelectedObject.renderer.material = HighlightedMat;
}
  As a result, we have got a scene, in which user by means of left mouse button can select objects and that object will be highlighted to red color. 

Remarks

  As I mentioned, I don’t think that described above way is the best way to highlight the object. I consider that the more efficient and flexible way to highlight the object is to use shader with «Emissive» component. This component is multiplied to result color by special way, so the object gets only “coloring” of red color but not the full red color. You can do such shader yourself, and in further articles I will tell you how to do it. From existing set of Unity3d shaders I found one that can fit to highlight purpose in some situations. It’s named «Reflective/Diffuse». You can use this shader and its field “Reflection Color” instead of “Emissive”.


Results of development you can see here:
https://dl.dropbox.com/u/20023505/Articles/Unity3d/Lesson3/WebPlayer/WebPlayer.html

As always for free you can download source codes from here:
https://dl.dropbox.com/u/20023505/Articles/Unity3d/Lesson3/Sources/Sources.zip
https://github.com/den-potapenko/Unity3dArticles/tree/master/Lesson3

14 comments:

  1. Denis, I have one more question. When I try to import this project's scene into another project of mine, I get the following error and I'm not sure why: NullReferenceException: Object reference not set to an instance of an object.

    ReplyDelete
    Replies
    1. The error happens on this line: (AppRoot.cs)

      // instantiate TransformObject and sets its rotate around object
      mTransform = new TransformObject();
      mTransform.SetTransformRotateAround(mGOFlat.transform);

      and (TransformObject.cs)
      Vector3 dir = mMoveObject.position - Camera.main.transform.position;

      Delete
    2. Hi, Lady_Eowyn!
      For using scene in another project you cannot just copy it, because in such a way the projects lost all references to scene objects, and this may cause such errors. You should create an empty prefab in your assets, then move all objects from the scene to this prefab, and then just press "Export package". Finally, import this package to another project.

      If you still cannot understand how to export and import the packages, please let me know, i'll create the package for you, and give you a link.



      Delete
  2. Hi Denis,

    Thank you so much for your help! I got it up and running.

    I love your tutorials and thank you so much for providing feedback and troubleshooting so quickly!

    I'm going to learn how to manipulate the camera and hope it goes well.

    ReplyDelete
    Replies
    1. Lady_Eowyn,

      Thanks :) Feel free to ask any questions!

      Delete
  3. Hey, I was wondering how would I be able to integrate a unity scene inside my web application and control my unity scene using javascript outside of unity and from my web app? It would be nice to see a basic tutorial on this subject.

    ReplyDelete
    Replies
    1. Hi, Lady_Eowyn!

      Very interesting idea! But as far as i remember Unity3d is able to call javascript, not javascript calls Unity3d. But the idea is really interesting and i'll check the implementation of this issue. Thanks Lady_Eowyn for new idea, and let me check. I'll post a comment about my results.

      Delete
  4. hi can u please help me I cant import this project to my project

    ReplyDelete
    Replies
    1. Hi eliseo montefalcon,

      What the problem?

      Delete
    2. hi denis I am making human body anatomy and I got your code to highlight the object .But the problem is I want to maintain the oirginal texture of the object after I click the object .can un help me?please....

      Delete
    3. Yes, i think i can. Please give the sample of your project, i'll do it exactly in it

      Delete
  5. This comment has been removed by the author.

    ReplyDelete
  6. thanks for your help I already solve the problem

    ReplyDelete