our rambling
Want to be notified of new blog posts?

Elwin Verploegen

Tutorial: Extending The Unity3D Editor December 22, 2014

By Elwin

Extending the Unity3D editor is one of the best features of the engine. If you want to see how this is useful in an actual project, you can read our blog post on our Dialogue Editor or Interaction Tool. In this tutorial I will teach you the basics on how to create your own editor scripts, which will hopefully get you going in creating amazing tools for your project. I’ll be doing this in Unity 5.0.0b17, but anything here should work just fine in Unity 4.6 (and probably below that as well). Whenever I add or change code to a script I will highlight it in a dark green.

I’ll be going through all the steps with you to get your first editor script going, this will include creating the window and modifying variables of a script on an object in the scene. If anything is unclear please send me a tweet and I’ll try to help you out.

Getting started

Start by creating a new project and adding a folder named Editor in the Assets directory, this is where we’ll be working from most of the time. Let’s create a file named ObjectSetter in the Editor folder, open that and drop in the following code:

using UnityEditor;
using System.Collections;

public class ObjectSetter : EditorWindow {

}

The above code is the start of any editor window script. A difference with a regular script is that instead of using UnityEngine; we are using UnityEditor;. This allows us to use the UnityEditor namespace. Another point to remember is that instead of extending MonoBehaviour (like a regular script would do), we’re extending EditorWindow. Before we continue make sure that your Project panel looks like this:

Unity3D Project Panel

Current project panel.

 Adding a menu button

Next up is adding a button in the toolbar at the top of the window. We’ll be adding a button under the Tools button, so that you’ll have to click on Tools first, followed by ObjectSetter. This is how that will look:

Toolbar

Toolbar

using UnityEditor;
using System.Collections;

public class ObjectSetter : EditorWindow {

    [MenuItem ("Tools/ObjectSetter")] //Add a menu item to the toolbar
    static void OpenWindow(){
    }
}

The first line of code that was added creates a new menu item in the tool bar, which is fairly dynamic. You can add pretty much type any path there and Unity will generate the button for you. You can even add a shortcut for the button. You can read how to do that in the Unity documentation over here.

Create a custom window

Now that we have a button in the toolbar, we can create a window and add a button in it. Let’s start with creating a window.

using UnityEditor;
using System.Collections;

public class ObjectSetter : EditorWindow {

    public static ObjectSetter window;

    [MenuItem ("Tools/ObjectSetter")]
    public static void OpenWindow(){
        window = (ObjectSetter)EditorWindow.GetWindow(typeof(ObjectSetter)); //create a window
        window.title = "Object Setter"; //set a window title
    }
}

First we create a new variable called window, this will (as the name suggests) hold the window. In the OpenWindow function we added 2 lines of the code. The first line calls EditorWindow.GetWindow(), this function returns the first EditorWindow of type ObjectSetter. If it doesn’t exist it will create one and return that instance. We then save that in our window variable. Try changing the title of the window to something else and see what happens.

If you just changed the code and nothing happened, you are absolutely correct. The OpenWindow function is only called when you press the button in the toolbar. This means that whenever you make a change you will have to press the button to make sure you can see the change. This is obviously very annoying when trying to debug, so let’s start with adding some code to fix that.

using UnityEditor;
using System.Collections;

public class ObjectSetter : EditorWindow {

    public static ObjectSetter window;

    [MenuItem ("Tools/ObjectSetter")]
    public static void OpenWindow(){
        window = (ObjectSetter)EditorWindow.GetWindow(typeof(ObjectSetter)); //create a window
        window.title = "Object Setter"; //set a window title
    }

    void OnGUI(){
        if(window == null)
            OpenWindow();
    }
}

Here we added the OnGUI function (you’re probably familiar with this function, as it can also be used in regular scripts). We’ll add a little trick where we check if the window variable is set to null, if it’s null we call OpenWindow again, setting the default variables again. Every time Unity compiles script, it will reset static variables. Whenever you change some code all you have to do is click on the editor window to focus it to reload the code.

Now that we have the OnGUI function, we can add a button to it. To create a button we have to use the normal GUI.Button function. To do so we have to include the UnityEngine namespace. You can use all of the regular GUI functions, but you also have access to the EditorGUI functions, which we’ll use a bit later. This is how it will look for now:

Editor Window button

Editor Window button

using UnityEditor;
using UnityEngine;
using System.Collections;

public class ObjectSetter : EditorWindow {

    public static ObjectSetter window;

    [MenuItem ("Tools/ObjectSetter")]
    public static void OpenWindow(){
        window = (ObjectSetter)EditorWindow.GetWindow(typeof(ObjectSetter)); //create a window
        window.title = "Object Setter"; //set a window title
    }

    void OnGUI(){
        if(window == null)
            OpenWindow();

        if(GUI.Button(new Rect(0, 0, position.width, position.height), "Press me")){
            Debug.Log("hodor");
        }
    }
}

With those lines added you should now see a button that fills up the window, and when you press it you should see hodor printed in your console. To get the size of the window, we use position.width & position.height, these are both properties of EditorWindow.position.

Create placeholder scripts & objects

Before we continue with actually modifying variables of an object through our script, let’s start by adding a cube to the scene and applying a script to it named DataHolder, this is just a script where we can modify some things to show that it all works. DataHolder looks like this:

using UnityEngine;
using System.Collections;

public class DataHolder : MonoBehaviour {

    public GameObject cam;
    public int health;
    public string username;

}

This is now how my scene hierarchy & project panel looks. Don’t forget to add the DataHolder script to the Cube!

Hierarchy & Project Panels

Hierarchy & Project Panels

Modifying the DataHolder variables

Here is where it gets a bit more complex, as we need to write a bit more code to get things working. Let’s start by grabbing the currently selected object so that we can manipulate it.

using UnityEditor;
using UnityEngine;
using System.Collections;

public class ObjectSetter : EditorWindow {

    public static ObjectSetter window;
    private static GameObject obj;

    [MenuItem ("Tools/ObjectSetter")]
    public static void OpenWindow(){
        window = (ObjectSetter)EditorWindow.GetWindow(typeof(ObjectSetter)); //create a window
        window.title = "Object Setter"; //set a window title
}

    void OnGUI(){
        if(window == null)
            OpenWindow();

        if(Selection.activeGameObject != null){
            //gets the object you currently have selected in the scene view
            obj = Selection.activeGameObject;
            GUI.Label(new Rect(0, 0, position.width, 25), "Current selected object: " + obj.name);
        }
    }

    void Update(){
        Repaint();
    }
}

I took out the button, because we won’t be needing that anymore. At the top I’ve added a variable called obj, I like to have this for easy access throughout this script (I usually have to refer to this quite often, so doing less typing is favourable to me). In the OnGUI function we then make use of the very handy Selection.activeGameObject. This variable holds the GameObject that you have currently selected in the scene.

Next, we check if you actually have something selected and then set obj to that object. After that we simply print the name of that object in the window using the regular GUI.Label.

We also added an Update function, this functions the same as in a regular script and will be called every frame. The Repaint function in there makes sure that our custom window is redrawn every frame. Click on different objects in the scene and watch the text in the window change. If you try commenting out the repaint function you will notice that the text only changes when you focus back on the window.

Now that we have the object we can move on and start modifying the variables in DataHolder. This is how it should look after applying the modifications below:

Editor Window with variable fields

Editor Window with variable fields

using UnityEditor;
using UnityEngine;
using System.Collections;

public class ObjectSetter : EditorWindow {

    public static ObjectSetter window;
    private static GameObject obj;

    [MenuItem ("Tools/ObjectSetter")]
    public static void OpenWindow(){
        window = (ObjectSetter)EditorWindow.GetWindow(typeof(ObjectSetter)); //create a window
        window.title = "Object Setter"; //set a window title
    }

    void OnGUI(){
        if(window == null)
            OpenWindow();

        if(Selection.activeGameObject != null){
            //gets the object you currently have selected in the scene view
            obj = Selection.activeGameObject;
            GUI.Label(new Rect(5, 0, position.width-10, 25), "Current selected object: " + obj.name);

            //make sure to only show the interface
            DataHolder comp = obj.GetComponent<DataHolder>();
            if(comp != null){
                comp.health = EditorGUI.IntField(
                    new Rect(5, 30, position.width-10, 16), 
                    "Health", 
                    comp.health
                );
                comp.username = EditorGUI.TextField(
                    new Rect(5, 50, position.width-10, 16), 
                    "User Name", 
                    comp.username
                );
                comp.cam = (GameObject) EditorGUI.ObjectField(
                    new Rect(5, 70, position.width-10, 16), 
                    "Camera GameObject", 
                    comp.cam, 
                    typeof(GameObject)
                );
            }
        }
    }

    void Update(){
        Repaint();
    }
}

We first try to get the DataHolder component from the selected object. If it has one, we show 3 EditorGUI fields. These generally have the same layout and will become familiar pretty fast, a list of all possibilities can be found over at the EditorGUI documentation. The 3rd variable field requires most work, but is very useful. You can essentially put any object type in there, this allows you to grab components from GameObjects (e.g. scripts, colliders, renderers, etc.).

To see this working, just select any object with the DataHolder script attached to it. Now change some of the variables or drag in a GameObject into the “Camera GameObject” field. Keep an eye on the inspector and you should see those variables change in real-time. Congratulations, you just created your first Editor script and are now ready to conquer the world! Let’s add some more small features though.

Create DataHolder

When the user selects a cube that doesn’t have a script we don’t want to manually drag in the script on the GameObject. Instead, we’ll add a button to add the component to the GameObject.

using UnityEditor;
using UnityEngine;
using System.Collections;

public class ObjectSetter : EditorWindow {

    public static ObjectSetter window;
    private static GameObject obj;

    [MenuItem ("Tools/ObjectSetter")]
    public static void OpenWindow(){
        window = (ObjectSetter)EditorWindow.GetWindow(typeof(ObjectSetter)); //create a window
        window.title = "Object Setter"; //set a window title
    }

    void OnGUI(){
        if(window == null)
            OpenWindow();

        if(Selection.activeGameObject != null){
            //gets the object you currently have selected in the scene view
            obj = Selection.activeGameObject;
            GUI.Label(new Rect(5, 5, position.width-10, 25), "Current selected object: " + obj.name);

            //make sure to only show the interface
            DataHolder comp = obj.GetComponent<DataHolder>();
            if(comp != null){
                comp.health = EditorGUI.IntField(
                    new Rect(5, 30, position.width-10, 16), 
                    "Health", 
                    comp.health
                );
                comp.username = EditorGUI.TextField(
                    new Rect(5, 50, position.width-10, 16), 
                    "User Name", 
                    comp.username
                );
                comp.cam = (GameObject) EditorGUI.ObjectField(
                    new Rect(5, 70, position.width-10, 16), 
                    "Camera GameObject", 
                    comp.cam, 
                    typeof(GameObject)
                );
            }
            else{
                if(GUI.Button(new Rect(5, 30, position.width-10, position.height-40), 
                "Add DataHolder")){
                    obj.AddComponent<DataHolder>();
                }
            }
        }
    }

    void Update(){
        Repaint();
    }
}

Now try creating a new cube and select that. The window should now show 1 big button that says “Add DataHolder”. Pressing that will add a component to the selected object, namely DataHolder. Once you press the button, it will disappear and the regular interface will show up again, this is because every frame it will check if the current object has the DataHolder component selected.

Conclusion

If you did everything as instructed, you should now have 1 window that looks like the left one when you have a normal GameObject selected. When you select an object with a DataHolder you will see the screen on the right.

The final result

The final result

That’s it for this blog. This knowledge should be enough to create very powerful editor scripts (you can probably recreate 90% of my Interaction Editor with just the above knowledge). Next time I’ll show you some useful functions to make everything a bit prettier and easier to work with from a user perspective.

If you have any questions or remarks, don’t hesitate to get in touch with me via Twitter.

Let’s add some more features in the next part in this tutorial.

Elwin Verploegen

Tutorial: Extending The Unity3D Editor Even More December 22, 2014

By Elwin

Last time we made a custom window and got it to interact with a GameObject. This time we’ll go a bit further and start implementing some more features there. Our starting off point will be the previous tutorial, so be sure to check that out first (over here).

Chaining events

Something that’s particularly useful for Fragments of Him, is the chaining of certain events after the previous one triggered. Keeping track of them however is a massive pain. We’ll add some functionality to our DataHolder to allow for chaining and then work all of that into our custom window to make it look all fancy. Let’s start by adding some simple functionality to allow for chaining in the DataHolder.

using UnityEngine;
using System.Collections;

public class DataHolder : MonoBehaviour {
    	public DataHolder previous;
    	public DataHolder next;
    	public bool enableonload = false;
}

We’ll be linking interactions together by dragging DataHolders into the previous and next variables. The enableonload boolean is used to make sure you can tag which interaction is the first one. We’ve removed the other variables, because we don’t need those anymore (they were just there for show). Let’s make sure the new variables show up in the editor.

using UnityEditor;
using UnityEngine;
using System.Collections;

    public class ObjectSetter : EditorWindow {

    public static ObjectSetter window;
    private static GameObject obj;

    [MenuItem ("Tools/ObjectSetter")]
    public static void OpenWindow(){
        window = (ObjectSetter)EditorWindow.GetWindow(typeof(ObjectSetter));  //create a window
        window.title = "Object Setter";	//set a window title
    }

    void OnGUI(){
        if(window == null)
            OpenWindow();

        if(Selection.activeGameObject != null){
            //gets the object you currently have selected in the scene view
            obj = Selection.activeGameObject;
            GUI.Label(new Rect(5, 5, position.width-10, 25), "Current selected object: " + obj.name);

            //make sure to only show the interface 
            DataHolder comp = obj.GetComponent<DataHolder>();
            if(comp != null){
                comp.next = (DataHolder) EditorGUI.ObjectField(
                    new Rect(5, 50, position.width-10, 16),
                    "Next Object",
                    comp.next, typeof(DataHolder), true
                );

                comp.enableonload = EditorGUI.Toggle(
                    new Rect(5, 70, position.width-10, 16), 
                    "Enable On Load", 
                    comp.enableonload
                );
            }
            else{
                if(GUI.Button(new Rect(5, 30, position.width-10, position.height-40), 
                    "Add DataHolder")){
                    obj.AddComponent<DataHolder>();
                }
            }
        }
    }

    void Update(){
        Repaint();
    }
}

The added code also uses the EditorGUI.ObjectField, but instead of using a GameObject type it will use a DataHolder type. Whenever you drag in a GameObject from the scene into that field it will automatically grab the DataHolder component. We also added an EditorGUI.Toggle, which does uses the same formatting as pretty much any other EditorGUI field. This uses a boolean to create a toggle box.

Displaying the chain

Next up we’ll want to show how all of these link to each other. We’ll start by adding this to the editor in a simple list.

using UnityEditor;
using UnityEngine;
using System.Collections;

    public class ObjectSetter : EditorWindow {

    public static ObjectSetter window;
    private static GameObject obj;

    [MenuItem ("Tools/ObjectSetter")]
    public static void OpenWindow(){
        window = (ObjectSetter)EditorWindow.GetWindow(typeof(ObjectSetter));  //create a window
        window.title = "Object Setter";	//set a window title
    }

    void OnGUI(){
        if(window == null)
            OpenWindow();

        if(Selection.activeGameObject != null){
            //gets the object you currently have selected in the scene view
            obj = Selection.activeGameObject;
            GUI.Label(new Rect(5, 5, position.width-10, 25), "Current selected object: " + obj.name);

            //make sure to only show the interface 
            DataHolder comp = obj.GetComponent<DataHolder>();
            if(comp != null){
                comp.next = (DataHolder) EditorGUI.ObjectField(
                    new Rect(5, 50, position.width-10, 16),
                    "Next Object",
                    comp.next, typeof(DataHolder), true
                );

                comp.enableonload = EditorGUI.Toggle(
                    new Rect(5, 70, position.width-10, 16), 
                    "Enable On Load", 
                    comp.enableonload
                );
            }
            else{
                if(GUI.Button(new Rect(5, 30, position.width-10, 70), 
                    "Add DataHolder")){
                    obj.AddComponent<DataHolder>();
                }
            }
        }

        int i = 0;
        foreach(DataHolder dataobj in GameObject.FindObjectsOfType(typeof(DataHolder))){
            if(GUI.Button(new Rect(5, 110 + i * 20, position.width-10, 16), dataobj.gameObject.name)){
                Selection.activeGameObject = dataobj.gameObject;
            }
            i++;
        }
    }

    void Update(){
        Repaint();
    }
}

What we’re doing here is looping through all DataHolder components in the current scene, this is done using GameObject.FindObjectsOfType. We then create a button for each of those. Whenever you click on one of these buttons, we use the previously used Selection.activeGameObject to set the currently selected GameObject. We will also change the “Add DataHolder” button to be smaller, as to not obstruct the list.

Automating the previous variable

We still have that previous variable in the DataHolder component, and we could manually set that variable in the editor window if we wanted to, but that defeats the purpose of writing custom tools. So let’s automate that. This is how my current hierarchy looks (nothing changed in the project panel so far):

Project Hierarchy

Project Hierarchy

Each of those cubes are the same as the one we made in the previous tutorial, except that they were renamed for usability.

using UnityEditor;
using UnityEngine;
using System.Collections;

    public class ObjectSetter : EditorWindow {

    public static ObjectSetter window;
    private static GameObject obj;
    private static DataHolder data;

    [MenuItem ("Tools/ObjectSetter")]
    public static void OpenWindow(){
        window = (ObjectSetter)EditorWindow.GetWindow(typeof(ObjectSetter));  //create a window
        window.title = "Object Setter";	//set a window title
    }

    void OnGUI(){
        if(window == null)
            OpenWindow();

        if(Selection.activeGameObject != null){
            //gets the object you currently have selected in the scene view
            obj = Selection.activeGameObject;
            GUI.Label(new Rect(5, 5, position.width-10, 25), "Current selected object: " + obj.name);

            //make sure to only show the interface 
            data = obj.GetComponent<DataHolder>();
            if(data != null){
                DataHolder tmpdata = data.next;
                EditorGUI.BeginChangeCheck();
                data.next = (DataHolder) EditorGUI.ObjectField(
                    new Rect(5, 50, position.width-10, 16),
                    "Next Object",
                    data.next, typeof(DataHolder), true
                );
                if(EditorGUI.EndChangeCheck()){
                    if(data.next != null){
                        data.next.previous = obj.GetComponent<DataHolder>();
                    }
                    else{
                        tmpdata.previous = null;
                    }
                }

                data.enableonload = EditorGUI.Toggle(
                    new Rect(5, 70, position.width-10, 16), 
                    "Enable On Load", 
                    data.enableonload
                );
             }
            else{
                if(GUI.Button(new Rect(5, 30, position.width-10, 70), 
                    "Add DataHolder")){
                    obj.AddComponent<DataHolder>();
                }
            }

            int i = 0;
            foreach(DataHolder dataobj in GameObject.FindObjectsOfType(typeof(DataHolder))){
                if(GUI.Button(new Rect(5, 110 + i * 20, position.width-10, 16), 
                    dataobj.gameObject.name)){
                    Selection.activeGameObject = dataobj.gameObject;
                }
                i++;
            }
        }        
    }

    void Update(){
        Repaint();
    }
}

The main thing we added here is the EditorGUI.BeginChangeCheck function, one of the very useful functions that you have access from with the EditorGUI (you could easily manually create the same functionality, but this makes your code more readable). The BeginChangeCheck function will check if anything changed in the GUI between the BeginChangeCheck and EndChangeCheck, if it does it will return true.

We use this to start setting the previous variable. If the next variable isn’t set we will set the previous variable to null, this will be used when the user removes the next variable from the currently selected object. To do so we have to keep a reference of data.next, this would otherwise give you an error when you try to try to set the previous variable of that to null. When the next variable is set to something, it will set the previous variable to the currently selected object.

It’s important to point out here that we’ve also added a variable called “data” at the top of the script. Just like obj, this will keep track of the currently selected DataHolder.

I would like to point out an alternative way to creating the if – else statement. This is more concise and may or may not be easier for you to read. Remember that both of these statements will do the exact same thing:

if(comp.next != null)
    comp.previous = obj.GetComponent();
else 
    comp.previous = null;

--------------------------------------------------------

comp.previous = (comp.next != null) ? obj.GetComponent<DataHolder>() : null;

You could (and probably should) add additional checks to see if, for example, the user isn’t trying to set the next variable to itself (which could potentially crash the system). Let’s also highlight the button that’s next & previous in queue when you have an object selected, this makes it easier for the user to see the flow of the components. and allows you to quickly click through interactions to see what’s happening.

using UnityEditor;
using UnityEngine;
using System.Collections;

    public class ObjectSetter : EditorWindow {

    public static ObjectSetter window;
    private static GameObject obj;
    private static DataHolder data;

    [MenuItem ("Tools/ObjectSetter")]
    public static void OpenWindow(){
        window = (ObjectSetter)EditorWindow.GetWindow(typeof(ObjectSetter));  //create a window
        window.title = "Object Setter";	//set a window title
    }

    void OnGUI(){
        if(window == null)
            OpenWindow();

        if(Selection.activeGameObject != null){
            //gets the object you currently have selected in the scene view
            obj = Selection.activeGameObject;
            GUI.Label(new Rect(5, 5, position.width-10, 25), "Current selected object: " + obj.name);

            //make sure to only show the interface 
            data = obj.GetComponent<DataHolder>();
            if(data != null){
                DataHolder tmpdata = data.next;
                EditorGUI.BeginChangeCheck();
                data.next = (DataHolder) EditorGUI.ObjectField(
                    new Rect(5, 50, position.width-10, 16),
                    "Next Object",
                    data.next, typeof(DataHolder), true
                );
                if(EditorGUI.EndChangeCheck()){
                    if(data.next != null){
                        data.next.previous = obj.GetComponent<DataHolder>();
                    }
                    else{
                        tmpdata.previous = null;
                    }
                }

                data.enableonload = EditorGUI.Toggle(
                    new Rect(5, 70, position.width-10, 16), 
                    "Enable On Load", 
                    data.enableonload
                );
             }
            else{
                if(GUI.Button(new Rect(5, 30, position.width-10, 70), 
                    "Add DataHolder")){
                    obj.AddComponent<DataHolder>();
                }
            }

            int i = 0;
            foreach(DataHolder dataobj in GameObject.FindObjectsOfType(typeof(DataHolder))){
                 if(data != null){
                    if(data.previous != null && data.previous == dataobj){
                        GUI.backgroundColor = new Color(1f, 0f, 0f);
                    }
                    else if(data.next != null && data.next == dataobj){
                        GUI.backgroundColor = new Color(0f, 1f, 0f);
                    }
                    else if(dataobj == data){
                        GUI.backgroundColor = new Color(0f, 0f, 0f);
                    }
                }

                if(GUI.Button(new Rect(5, 110 + i * 20, position.width-10, 16), 
                    dataobj.gameObject.name)){
                    Selection.activeGameObject = dataobj.gameObject;
                }

                GUI.backgroundColor = new Color(1f, 1f, 1f);
                i++;
            }
        }        
    }

    void Update(){
        Repaint();
    }
}

Time to give the user some feedback! We’ve added some code that changes the background colour of the GUI using GUI.backgroundColor. Whenever the previous DataHolder is found, the colour of the button will become red. Whenever it is the same as the next variable it will turn green, when the loop hits the currently selected object, it will make that button black. At the end of each cycle it will reset the background color back to white (if you don’t do this, every button afterwards will remain the colour of whatever you last set the GUI.backgroundColor to).

You should end up with the following window:

Final Editor Window

Final Editor Window

In this case Cube 1 links to Cube 2 and Cube 2 links to Cube 3. If Cube 2 is selected the above window should look like the custom editor window.

If you have any questions or remarks, don’t hesitate to get in touch with me via Twitter.

Elwin Verploegen

Snippet: Drawing Lines In Editor Windows December 19, 2014

By Elwin

This script was written by Yossarian King and Linusmartensson.

This script makes it easy to draw lines in editor windows, which is perfect for creating separator lines or showing connections between nodes. Here’s an example of that from our dialogue editor:

Line drawing in editor window

Line drawing in editor window

using System.Reflection;
using UnityEngine;
 
// Line drawing routine originally courtesy of Linusmartensson:
// http://forum.unity3d.com/threads/71979-Drawing-lines-in-the-editor
//
// Rewritten to improve performance by Yossarian King / August 2013.
//
// This version produces virtually identical results to the original (tested by drawing
// one over the other and observing errors of one pixel or less), but for large numbers
// of lines this version is more than four times faster than the original, and comes
// within about 70% of the raw performance of Graphics.DrawTexture.
//
// Peak performance on my laptop is around 200,000 lines per second. The laptop is
// Windows 7 64-bit, Intel Core2 Duo CPU 2.53GHz, 4G RAM, NVIDIA GeForce GT 220M.
// Line width and anti-aliasing had negligible impact on performance.
//
// For a graph of benchmark results in a standalone Windows build, see this image:
// https://app.box.com/s/hyuhi565dtolqdm97e00
 
public static class Drawing
{
    private static Texture2D aaLineTex = null;
    private static Texture2D lineTex = null;
    private static Material blitMaterial = null;
    private static Material blendMaterial = null;
    private static Rect lineRect = new Rect(0, 0, 1, 1);
 
    // Draw a line in screen space, suitable for use from OnGUI calls from either
    // MonoBehaviour or EditorWindow. Note that this should only be called during repaint
    // events, when (Event.current.type == EventType.Repaint).
    //
    // Works by computing a matrix that transforms a unit square -- Rect(0,0,1,1) -- into
    // a scaled, rotated, and offset rectangle that corresponds to the line and its width.
    // A DrawTexture call used to draw a line texture into the transformed rectangle.
    //
    // More specifically:
    //      scale x by line length, y by line width
    //      rotate around z by the angle of the line
    //      offset by the position of the upper left corner of the target rectangle
    //
    // By working out the matrices and applying some trigonometry, the matrix calculation comes
    // out pretty simple. See https://app.box.com/s/xi08ow8o8ujymazg100j for a picture of my
    // notebook with the calculations.
    public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color, float width, 
                                bool antiAlias)
    {
        // Normally the static initializer does this, but to handle texture reinitialization
        // after editor play mode stops we need this check in the Editor.
        #if UNITY_EDITOR
        if (!lineTex)
        {
            Initialize();
        }
        #endif
 
        // Note that theta = atan2(dy, dx) is the angle we want to rotate by, but instead
        // of calculating the angle we just use the sine (dy/len) and cosine (dx/len).
        float dx = pointB.x - pointA.x;
        float dy = pointB.y - pointA.y;
        float len = Mathf.Sqrt(dx * dx + dy * dy);
 
        // Early out on tiny lines to avoid divide by zero.
        // Plus what's the point of drawing a line 1/1000th of a pixel long??
        if (len < 0.001f)
        {
            return;
        }
 
        // Pick texture and material (and tweak width) based on anti-alias setting.
        Texture2D tex;
        Material mat;
        if (antiAlias)
        {
            // Multiplying by three is fine for anti-aliasing width-1 lines, but make a wide "fringe"
            // for thicker lines, which may or may not be desirable.
            width = width * 3.0f;
            tex = aaLineTex;
            mat = blendMaterial;
        }
        else
        {
            tex = lineTex;
            mat = blitMaterial;
        }
 
        float wdx = width * dy / len;
        float wdy = width * dx / len;
 
        Matrix4x4 matrix = Matrix4x4.identity;
        matrix.m00 = dx;
        matrix.m01 = -wdx;
        matrix.m03 = pointA.x + 0.5f * wdx;
        matrix.m10 = dy;
        matrix.m11 = wdy;
        matrix.m13 = pointA.y - 0.5f * wdy;
 
        // Use GL matrix and Graphics.DrawTexture rather than GUI.matrix and GUI.DrawTexture,
        // for better performance. (Setting GUI.matrix is slow, and GUI.DrawTexture is just a
        // wrapper on Graphics.DrawTexture.)
        GL.PushMatrix();
        GL.MultMatrix(matrix);
        Graphics.DrawTexture(lineRect, tex, lineRect, 0, 0, 0, 0, color, mat);
        GL.PopMatrix();
    }
 
    public static void DrawBezierLine(Vector2 start, Vector2 startTangent, Vector2 end, 
                                      Vector2 endTangent, Color color, float width, 
                                      bool antiAlias, int segments)
    {
        Vector2 lastV = CubeBezier(start, startTangent, end, endTangent, 0);
        for (int i = 1; i < segments; ++i)
        {
            Vector2 v = CubeBezier(start, startTangent, end, endTangent, i/(float)segments);
            Drawing.DrawLine(lastV, v, color, width, antiAlias);
            lastV = v;
        }
    }
 
    private static Vector2 CubeBezier(Vector2 s, Vector2 st, Vector2 e, Vector2 et, float t)
    {
        float rt = 1 - t;
        return rt * rt * rt * s + 3 * rt * rt * t * st + 3 * rt * t * t * et + t * t * t * e;
    }
 
    // This static initializer works for runtime, but apparently isn't called when
    // Editor play mode stops, so DrawLine will re-initialize if needed.
    static Drawing()
    {
        Initialize();
    }
 
    private static void Initialize()
    {
        if (lineTex == null)
        {
            lineTex = new Texture2D(1, 1, TextureFormat.ARGB32, false);
            lineTex.SetPixel(0, 1, Color.white);
            lineTex.Apply();
        }
        if (aaLineTex == null)
        {
            aaLineTex = new Texture2D(1, 3, TextureFormat.ARGB32, false);
            aaLineTex.SetPixel(0, 0, new Color(1, 1, 1, 0));
            aaLineTex.SetPixel(0, 1, Color.white);
            aaLineTex.SetPixel(0, 2, new Color(1, 1, 1, 0));
            aaLineTex.Apply();
        }
 
        // GUI.blitMaterial and GUI.blendMaterial are used internally by GUI.DrawTexture,
        // depending on the alphaBlend parameter. Use reflection to "borrow" these references.
        blitMaterial = (Material)typeof(GUI).GetMethod(
    "get_blitMaterial", 
    BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);
        blendMaterial = (Material)typeof(GUI).GetMethod(
    "get_blendMaterial", 
    BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);
    }
}

To use this script, simply call the following function in the OnGUI function of an editor window.

DrawLine(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)

Where pointA is the starting point of the line, and pointB is the end of the line. This will draw a straight line between those points. To draw a curved line like in the image at the top of this post you can either use the DrawBezierLine function of the above script, or use a function that’s built into Unity (which is what I use in the above picture). The code below is what I use to draw those lines.

	float tangent = Mathf.Clamp((-1)*(start.x - end.x), -100, 100);
	Vector2 endtangent = new Vector2(end.x - tangent, end.y);
		
	UnityEditor.Handles.DrawBezier(
		start, 
		end,
		endtangent, 
		endtangent,
		new Color(0.88f, 0.73f, 0.19f, 0.5f),
		linetexture,
		width
	);

I haven’t done a performance test of the DrawBezierLine against the Unity DrawBezier, but if it works it works (and generally I don’t try to overthink performance when writing editor scripts).

Elwin Verploegen

Snippet: Automatically Add Scene Labels December 19, 2014

By Elwin

This script was written by Thundernerd on the Unity3D Answers Forum at the following link: http://answers.unity3d.com/questions/542890/scene-color-object-marking.html.

The following script allows you to automatically add a Label on a GameObject in a scene, here’s a screenshot on how that looks in the scene:

Scene Label

Scene Label

 using System;
 using System.Reflection;
 using UnityEditor;
 using UnityEngine;
 
 public class IconManager {
 
     public enum LabelIcon {
         Gray = 0,
         Blue,
         Teal,
         Green,
         Yellow,
         Orange,
         Red,
         Purple
     }
 
     public enum Icon {
         CircleGray = 0,
         CircleBlue,
         CircleTeal,
         CircleGreen,
         CircleYellow,
         CircleOrange,
         CircleRed,
         CirclePurple,
         DiamondGray,
         DiamondBlue,
         DiamondTeal,
         DiamondGreen,
         DiamondYellow,
         DiamondOrange,
         DiamondRed,
         DiamondPurple
     }
 
     private static GUIContent[] labelIcons;
     private static GUIContent[] largeIcons;
 
     public static void SetIcon( GameObject gObj, LabelIcon icon ) {
         if ( labelIcons == null ) {
             labelIcons = GetTextures( "sv_label_", string.Empty, 0, 8 );
         }
 
         SetIcon( gObj, labelIcons[(int)icon].image as Texture2D );
     }
 
     public static void SetIcon( GameObject gObj, Icon icon ) {
         if ( largeIcons == null ) {
             largeIcons = GetTextures( "sv_icon_dot", "_pix16_gizmo", 0, 16 );
         }
 
         SetIcon( gObj, largeIcons[(int)icon].image as Texture2D );
     }
 
     private static void SetIcon( GameObject gObj, Texture2D texture ) {
         var ty = typeof( EditorGUIUtility );
         var mi = ty.GetMethod( "SetIconForObject", BindingFlags.NonPublic | BindingFlags.Static );
         mi.Invoke( null, new object[] { gObj, texture } );
     }
 
     private static GUIContent[] GetTextures( string _b, string postFix, int si, int c ) {
         GUIContent[] guiContentArray = new GUIContent[c];
 
         var t = typeof( EditorGUIUtility );
         var mi = t.GetMethod( 
                    "IconContent", 
                    BindingFlags.Public | BindingFlags.Static, 
                    null, 
                    new Type[] { typeof( string ) }, 
                    null 
                );
 
         for ( int index = 0; index < c; ++index ) {
             guiContentArray[index] = mi.Invoke( 
                                        null, 
                                        new object[] { 
                                                    _b + (object)(si + index) + postFix 
                                                }
                                    ) as GUIContent;
         }
 
         return guiContentArray;
     }
 }

Create a new file in the Editor directory named IconManager and copy the above code into it. If you want to set the label of a GameObject you can call the code below.

IconManager.SetIcon(obj, IconManager.LabelIcon.Blue);

Where obj is the GameObject that you want to give a label.

Elwin Verploegen

Fragments of Him – Creating The Interactions December 18, 2014

By Elwin

This article will cover the interaction tool that we use to build Fragments of Him. This tool has seen several iterations before arriving at its current state. When we started building Fragments of Him, one of the questions we asked was “How can we easily implement content and gameplay without writing a lot of custom code”. Back then, the answer was pretty simple. We write a custom interaction tool.

How it worked

When we first started, all we needed was a simple way to trigger dialogue from either a click or walking through an area. That wasn’t very difficult, and a simple script with a couple of variables would do the trick. Then we figured that we needed to play object animations, character animations, play audio, enable the next interaction, apply and drive a player feedback outline, force a player to move out of the way for the character animation and more. As you can probably see, this became quite chaotic rather quickly. After patching in all these features to the tool we started with our tool ended up looking like this:

Creating interactions in the editor.

Creating interactions in the editor.

Not only did it look very messy, it would require quite a bit of overhead in thinking of all the little quirks in the tool to implement an interaction. In more complex cases, we needed to trigger multiple animations, with several pieces of audio, play the accompanying dialogue audio and then indicate what interaction is next. Instead of trying to fix the tool we had, we opted to create a new version instead. This new version should be faster to use and be more dynamic.

Designing a Modular System

One of the things that became clear when developing this new system is that it should be modular in its use. If we need to trigger 40 things after a single click, that should not be a problem. The new system would have a single script (the base) that would keep track of all the modules of that interaction. In addition to that it should trigger the functions of all the individual modules. Each of the modules can then have its own systems for playing and deleting, allowing for an easier way to create, maintain and clean up modules. Here’s roughly how I planned it out:

Modular Interaction Editor Design

Modular interaction editor design

That seemed fairly simple and formed the starting point. We developed a couple of the basic modules to test out the callback system and gave it a whirl. While testing, we still used the inspector to drag in all the scripts and variables. While this isn’t too bad of a solution, it doesn’t give us any overview of the entire system. We figured that developing an interface around the new interaction editor would give us the overview we wanted.

Interaction Editor

Interaction Editor. All possible modules are on the left, currently added modules & parameters on the right.

The above screenshot is how the tool currently looks. Whenever we need to add a new module, all we have to do is add a button once it’s completed (and make sure the parameters show up properly) and it’s good to go. When an interaction is created, this is how it will show up in the scene:

Editor Interactions - Scene View

Interactions as shown in the scene view

To indicate where all these interactions take place we use the Unity’s colored icons. This makes it easy to identify where an interaction takes place. Additionally, it also makes it easier to find an interaction when looking for one in the scene. Sometimes we would forget to add an icon to an interaction and that could get confusing pretty quickly. Not only do we now automatically add an icon when creating an interaction, we also create and update a list that keeps track of all the interactions in the scene. To modify an interaction before this new tool, we had to find the interaction in the scene hierarchy or click on the icon in the scene if we thought of adding it. That turned out to be very frustrating and inefficient when trying to quickly check multiple interactions in a scene. The solution to this was to add a list to the interaction editor that keeps track of all the interactions in a scene. The currently selected interaction is coloured brown, the interactions that will enable after completing the interaction are marked as blue.

Modular interaction editor  with

Modular interaction editor with all of the scene interactions

Results

With our first (old) interaction tool, we spent a couple of days trying to get all the interactions in the first scenes (while adding features in meantime). This resulted into a lot of workarounds that were needed to get around limitations. The new system took a week (give or take) to build, and instead of spending a day to put in interactions, we can now do the same amount of implementation in roughly 2 hours.

If you have any questions or remarks, don’t hesitate to get in touch with me via Twitter.