Sunday, March 29, 2015

How To Use Editor Window To Make Custom Editors In Unity (With Sample code)

Hello, readers. Firstly, sorry for such a late post, I was busy for some months as my exams were going on and I had to Prepare for that. As the title says I will give you some information or hints about how to use The Editor Window class in unity to create your custom Editors.

Editor Window
A major strength of unity is to edit or customize or add new features to your workspace that fits your style or your needs. Unity does open the opportunity to do so with editor or editor window class. The Editor Window class enables you to create a custom window that can float free or be docked as a tab, just like the native windows in the Unity interface.

Creating Editor Window
You just need to make a class deriving from the Editor Window class and add a default method Init. e.g. - (You also need to add using UnityEditor; above class declaration to make unity recognize The EditorWindow class).

public class CustomWindow : EditorWindow 
{
[MenuItem("MyMenu/CustomWindow")]
static void Init () 
  {

  }
}


The MenuItem property specify the Menu item for making the window Appear. I've made a menu called MyMenu and a item named CustomWindow in it.
Now You need to add this line in Init method to make the window appear when clicked on the menu item. -

 static void Init ()
CustomWindow window = (CustomWindow)EditorWindow.GetWindow(typeof(CustomWindow));
}

Now save the script and when the compiling is finished in unity You will see your new Menu Item and when you click it you get a clear window which can be tabbed, docked etc.


Now, to add Something to it we need to add another method OnGUI inside your class like this -

void OnGUI () {

}

In this method, you can add buttons, labels, textfields etc. via GUILayout or EditorGUI (or EditorGUILayout) Class. Example -

void OnGUI () {
     GUILayout.Label("Example Text");
        if(GUILayout.Button("Example Button")){
            //Here you can add the code to run when button is clicked
        }
}

Now here is an example of a float field

private float val; //declare this in in class above all methods
void OnGUI()
    {
        GUILayout.Label("Example Text");
        if(GUILayout.Button("Example Button")){
            //Here you can add the code to run when button is clicked
        }  
        val = EditorGUILayout.FloatField("Example float input:", val);
    }


And finally here is the preview of our Editor Window in Action - 



OK The appearance is Fine But How about It's functioning 

Now we know how to add buttons, labels etc. in window about what to do with them i.e. How to make the function the task for which it's created. For that, We will have to first know what is the cause of making an editor window. 
Sometimes developers made them to make their work easier and faster, It's also used commercially to make plugins for unity etc.
To make use of Editor Window you can use any of unity classes to make the window do something freely. But I will show you how to achieve some basic uses of Custom Editors. -

1. Spawning GameObjects or Prefabs - You can use the Object.Instantiate to spawn or clone a game object in the current scene like this - 

 if(GUILayout.Button("Example Button")){
            GameObject obj = Instantiate(GameObject.Find("Cube"));
        }
  

But you need to use the "AssetDatabase.LoadAssetAtPath" function to get the instance of the prefab, then you can use the normal instantiate function. But as the prefab is type of 'object' class to instantiate it, you need to cast it to GameObject in order to spawn it in the current scene. -

 if(GUILayout.Button("Example Button")){
Object Prefab = AssetDatabase.LoadAssetAtPath("Assets/Prefabs/sphere.prefab", typeof(GameObject));
            GameObject obj = Instantiate(Prefab) as GameObject;
}
    
Note - You need to enter the full path of the Prefab. Like in the example above the prefab named "sphere" should be located in a folder named "Prefabs" and if it's not there it will throw an exception if the prefab is not found in given path.

2.Adding Components to Existing or new GameObjects - To add new components you can use GameObject.AddComponent to add new components to an object. Example -

 if (GUILayout.Button("Example Button"))
        {
            GameObject obj = Instantiate(GameObject.Find("Cube"));
            BoxCollider collider;
            collider = obj.AddComponent<BoxCollider>();
        }  

3.Raycasting Through Mouse position in the Scene View - This is used many times when creating custom plugins for unity or for any other purpose.
To achieve this We will use the -

OnSceneGUI function - This function lets the editor script handle events in scene view. As you should know that The Input class only works in real game not in editor. The inputs through keyboard and mouse are handled with "Event" class. According To Manual - In the OnSceneGUI you can do eg. mesh editing, terrain painting or advanced gizmos If call Event.current.Use(), the event will be "eaten" by the editor and not be used by the scene view itself.

(i) That's Good But How To Use It ?
First add The OnSceneGUI function with its default argument Scene View like this - 

 void OnSceneGUI(SceneView scene){
}

But adding this won't work, you need to add a delegate of OnSceneGUI function to be able to use it. For That Add these -

 void OnEnable()
    {
        SceneView.onSceneGUIDelegate += this.OnSceneGUI;
     }
    void OnDisable()
    {
       SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
    }

We added the delegate for our function when the Window is Enabled and removed the delegate when the window is closed to make it efficient.

(ii) Okay, now how about the raycasting?
To do raycasting through mouse we will use the "Event.current.mousePosition" to get the current mouse position and then cast a ray through it to get the point in space where the mouse is pointing at. code - 

 void OnSceneGUI(SceneView scene)

    {
            if (Event.current.type == EventType.mouseDown)
            {

                Ray grow = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                RaycastHit hit;
                if (Physics.Raycast(grow, out hit, 1000))
                {
                    Debug.Log(hit.point);
                }
            }
      }

Preview - 

Now you can Test your script. You may notice that raycasting does fine but when you click in the scene the object in front of it also get selected. It is no problem for us in this script but it may give problem when making something selected. So to prevent unity from selecting or deselecting objects, we need to add this line in OnSceneGUI -
 HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));

So the script now look like this - 

  void OnSceneGUI(SceneView scene)
    {

  HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));            if (Event.current.type == EventType.mouseDown)
            {
                Ray grow = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                RaycastHit hit;
                if (Physics.Raycast(grow, out hit, 1000))
                {
                    Debug.Log(hit.point);
                }
            }
      }

Now it's good and working as it should.

So, its time to end this little tutorial as it has already been very long. I hope you like it and if yes, please convey it through comments. Feel free to give an advice or ask something if you have any doubt. Thanks for reading.
Bye.  

1 comment: