Category Archives: Unity Tips
Supporting Both Unity Light and Dark Skins

Maybe you have an idea for a cool unique tool, and you want to put it on the Asset Store. But, what if you only have the free, or personal, edition of Unity, like I do. That means that you only have the light skin, right? Not…exactly. It would not make sense for Unity to write their UI code twice, once for light and again for dark, so they actually include both within every Editor installation.

Unity uses GUISkins for “Immediate Mode” (IMGUI) controls. This is also true for the Unity Editor itself. They even provided a way for us to access the built-in skins that they use.

public static GUISkin EditorGUIUtility.GetBuiltinSkin(EditorSkin skin);

This method takes an enum value and returns an instance of a GUISkin. The enum values are Game, Inspector, and Scene. These names are a bit misleading. They actually have no connection with the EditorWindows of the same names. At least, none that I can find. Pretty much everything that the Unity devs use to draw every part of every built-in EditorWindow can be found within these three GUISkins.

Okay, so we can get a reference to the built-in Unity GUISkins. So what? What’s that got to do with the light and dark skins? Well it turns out that the light skin corresponds to EditorSkin.Inspector, and the dark skin is EditorSkin.Scene. The skin for Game doesn’t actually have much in it of note.

So, we can get to the built-in skins that Unity uses for the Editor. Now we need to figure out how to use them. A GUISkin is simply a collection of named GUIStyles, along with a few settings that are used by some GUI controls. Some styles are accessible through properties, such as box, label, button, etc. There is also an array called customStyles. This can hold as many GUIStyles as you need. If you have a GUISkin asset (one can be created using the Create menu), you can add styles to the array and fill them out using the Inspector. To access any style within a skin, use the following method.

public GUIStyle GUISkin.GetStyle(string styleName);

This method takes the name of a style in the skin and returns the associated style. But, what are the style names that Unity is using? Is there a list somewhere? Nope. Besides, what we really need is some way to browse through all of the styles in the Inspector. There are a couple of ways to do this.

The first is very simple. Make the skin the selected object.

public static class InspectEditorSkins
{
	[MenuItem("Tools/Inspect Dark Skin")]
	public static void MenuItem_InspectDarkSkin()
	{
		Selection.activeObject = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene);
	}
	
	[MenuItem("Tools/Inspect Light Skin")]
	public static void MenuItem_InspectLightSkin()
	{
		Selection.activeObject = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);
	}
	
	[MenuItem("Tools/Inspect Game Skin")]
	public static void MenuItem_InspectGameSkin()
	{
		Selection.activeObject = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Game);
	}
}

Now, selecting one of the menu items will let us inspect each skin. Notice, though, that they are all showing as ‘disabled’ in the Inspector. This is because these assets are, in a way, locked. Read-only, you might say. It makes sense. You wouldn’t want just anyone to screw around with these if you were Unity.

What I have found easier to work with long term is to make copies of the skins and save them as assets. Here is the code I have been using.

public static class EditorSkinsExtractor
{
    [MenuItem("Assets/Extract Editor Skins")]
    public static void MenuItem_ExtractEditorSkins()
    {
		string unityVersion = Application.unityVersion;
		string skinsFolder = "EditorSkins_" + unityVersion;
		string fullPath = Application.dataPath + "/" + skinsFolder;

		if (!System.IO.Directory.Exists(fullPath))
		{
			AssetDatabase.CreateFolder("Assets", skinsFolder);
		}

		if (System.IO.Directory.Exists(fullPath))
		{
			string relativePath = "Assets/" + skinsFolder + "/EditorSkin-";

			ExtractSkin(relativePath, EditorSkin.Game);
			ExtractSkin(relativePath, EditorSkin.Inspector);
			ExtractSkin(relativePath, EditorSkin.Scene);
		}
		else
		{
			Debug.LogError("Failed to create folder");
		}
	}

	private static void ExtractSkin(string relativePath, EditorSkin skinType)
	{
		GUISkin skin = ScriptableObject.Instantiate(EditorGUIUtility.GetBuiltinSkin(skinType)) as GUISkin;
		AssetDatabase.CreateAsset(skin, relativePath + skinType + ".guiskin");
	}
}

This will create a folder for the Unity version currently running, and then create copies of the skins and save them as assets within the folder. Now you can peruse at will!

Now remember, the Scene skin is the dark skin and the Inspector skin is the light one. Select either and expand Custom Styles. Let’s find something familiar. As you scroll through the list, you’ll notice that the names are in alphabetical order. This is nice for when you’re looking for something specific. Scroll all the way down to “IN LockButton”. Can you guess what that might be? It’s the lock button from the IN-spector. Now take a look at “PR Insertion”. That is the blue line that shows up when you are moving items in the PR-oject view! Fun! Of course, that blue line is also used in other places, like the Hierarchy window.  I’m also using it directly in JumpTo, so that my window feels native.

An excerpt straight from JumpTo…

//create some public properties to hold your styles
public GUIStyle LinkLabelStyle { get; private set; }
public GUIStyle DragDropInsertionStyle { get; private set; }

//NOTE: you must work with GUIStyles within OnGUI()

GUISkin editorSkin = null;
if (EditorGUIUtility.isProSkin)
	editorSkin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene);
else
	editorSkin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);

//here we are creating copies of the built-in style, and then making
//  whatever changes may be necessary for our use-case
LinkLabelStyle = new GUIStyle(editorSkin.GetStyle("PR Label"));
LinkLabelStyle.name = "Link Label Style";
LinkLabelStyle.padding.left = 8;

DragDropInsertionStyle = new GUIStyle(editorSkin.GetStyle("PR Insertion"));
DragDropInsertionStyle.imagePosition = ImagePosition.ImageOnly;
DragDropInsertionStyle.contentOffset = new Vector2(0.0f, -16.0f);

The if statement will guarantee that we are pulling the correct style for the end-user’s version of Unity. Now your IMGUI code can be completely ignorant of the skin being used. In theory, anyway…

These styles can be passed as arguments to GUILayout methods, or used directly using the GUIStyle.Draw() method.

I always try to give credit where it is due. The inspiration came from this thread in the Unity forum, in a post from user dodo. I’d had the same question as the OP, Tonzie, and this was what was needed.

CustomEditor Frame Object On Double-Click

I’ve been wanting to make a public example of this ever since I found out how to do it. Here goes!

When you double-click a GameObject with a mesh, the Scene View will focus on that object and zoom to the mesh’s extents in the scene. That’s great for objects with a mesh, but how would you do it for other kinds of objects?

It’s actually pretty simple. As we know, for any MonoBehaviour based class, you can define a custom editor. Say you have a script that defines a set of points in local space (meaning Vector3 points that are relative to the object’s transform.position). When you double-click this object under normal circumstances, the Scene View will focus on it as expected, but you may not see all of the points you’ve defined. This is because the default inspector doesn’t know to take these points into account when trying to figure out how big your object is in world space.

So, you need to make a custom editor for your class. In that editor you will need to create two methods that the Scene View will look for when the object is double-clicked:

private bool HasFrameBounds()
private Bounds OnGetFrameBounds()

Returning true from HasFrameBounds tells the Scene View that there is an OnGetFrameBounds method. Return a Bounds object from OnGetFrameBounds to define the size of your object. In the case of the example, you would want the bounds to encapsulate all of the points as well as the object’s transform.position value.

Let’s look at an example class.

using UnityEngine;
using System.Collections;


public class FrameBoundsObject : MonoBehaviour
{
	[Range(0.01f, 2.0f)]
	[SerializeField] private float m_ExtentsMultiplier = 1.0f;
	[SerializeField] private bool m_HasFrameBounds = true;

	[SerializeField] private Vector3[] m_Points;

	public Vector3[] Points { get { return m_Points; } }
	public float ExtentsMultiplier { get { return m_ExtentsMultiplier; } }
	public bool HasFrameBounds { get { return m_HasFrameBounds; } }


	private void OnDrawGizmos()
	{
		Vector3 position = transform.position;

		Gizmos.color = Color.blue;
		Gizmos.DrawSphere(position, 1.0f);

		if (m_Points == null)
			return;

		Gizmos.color = Color.green;
		for (int i = 0; i < m_Points.Length; i++)
		{
			Gizmos.DrawSphere(position + m_Points[i], 1.0f);
		}
	}
}

It’s just a data class, so there’s no logic here. Using Gizmos, the object’s position is drawn as a blue sphere, and each point in the array of points is drawn as a green sphere. Let’s drop this into a scene and add a few points to it.

unitytips_framebounds01So, now we have six points all at 100m from the center where the object’s transform sits. At this point, we haven’t made a custom editor for the class so double-clicking the object will exhibit the default behavior. Now, double-click the object in the Hierarchy. The Scene View zooms in so that the blue sphere is in the center. But, all of the green spheres representing each point aren’t in view!

unitytips_framebounds02

Okay. Let’s add the custom editor so that the object bounds can be defined.

using UnityEditor;
using UnityEngine;
using System.Collections;


[CustomEditor(typeof(FrameBoundsObject))]
public class FrameBoundsEditor : Editor
{
	private FrameBoundsObject m_Target;
	private Transform m_Transform;

	
	private void OnEnable()
	{
		m_Target = target as FrameBoundsObject;
		m_Transform = m_Target.transform;
	}

	private bool HasFrameBounds()
	{
		return m_Target.HasFrameBounds;
	}

	private Bounds OnGetFrameBounds()
	{
		Bounds frameBounds = new Bounds();
		frameBounds.Encapsulate(m_Transform.position);

		if (m_Target.Points != null)
		{
			for (int i = 0; i < m_Target.Points.Length; i++)
			{
				frameBounds.Encapsulate(m_Target.Points[i]);
			}
		}

		frameBounds.extents *= m_Target.ExtentsMultiplier;
		
		return frameBounds;
	}
}

Take a look at the OnGetFrameBounds method body. Notice that it is creating a Bounds object and “bumping out” the extents using Encapsulate. Also, notice that the target’s transform.position is being encapsulated. This is because I wanted it to work this way for the example. If your object won’t need to take the transform into account, then don’t. The bounds can be whatever you need!

Now double-click the object. All of the green spheres should now be in the Scene View. Looks great, right? Well, not really.

unitytips_framebounds03It’s too far away! What’s up? It turns out that Unity, behind the scenes is…uhm…fixing the bounds that you return from OnGetFrameBounds. There are good reasons for doing this. The Editor is trying to make sure that your whole object is visible from every angle. But, the adjustments can yield unexpected results. Crack open ILSpy and look at the SceneView.FrameSelected method. Toward the bottom you’ll see that the bounds’ extents are being scaled by 1.5f and then again by 2.2f. Because of this, you may want to adjust the extents value to counteract the scaling a little.

Adjust the object’s Extents Multiplier value to 0.45 and double-click again. Everything should be much closer now.

unitytips_framebounds04Feel free to play with that value while changing the angle of the Scene View camera to see the effect of adjusting the extents by a static value. Also, toggle the Has Frame Bounds and double-click to see that when the HasFrameBounds method returns false, the frame bounds aren’t calculated when framing the object.

 

EditorWindow Modifier Keys

There’s an undocumented method you can implement that catches modifier key state changes called ModifierKeysChanged(). Any time the shift, ctrl, alt, win, or command keys go up or down, this method will be called.

using UnityEditor;
using UnityEngine;


public class ModifierKeysChangedEditorWindow : EditorWindow
{
	private void ModifierKeysChanged()
	{
		//NOTE: Event.current is null at this point, so
		//		it can't be used to check the state of
		//		the modifier keys

		Debug.Log("Modifier keys changed");
	}


	[MenuItem("Tools/Modifier Keys Window")]
	public static void Init_ToolsMenu()
	{
		EditorWindow window = EditorWindow.GetWindow<ModifierKeysChangedEditorWindow>();
		window.title = "Modifier Keys";
		window.Show();
	}
}

Underneath, the method is simply being added to EditorApplication.modifierKeysChanged automatically. This callback doesn’t pass any parameters, and Event.current is null at the time it is invoked. At this time, I can’t find any way to test which modifier key state actually changed.

A possible use-case is when the window should change somehow when shift is pressed/released, you could call Repaint() here. There are, however, other ways to handle modifier keys in an EditorWindow. You should keep in mind that this is undocumented, so it may cease to work with any Unity update in the future. As of 4.5.3p2, it works.

EditorWindow OnBecameVisible and OnBecameInvisible

OnBecameVisible and OnBecameInvisible are in the Unity docs as methods of the Renderer and MonoBehaviour classes. It turns out that they work for EditorWindows, too!

They get called when a docked window changes visibility; meaning that when a docked window is hidden by a sibling window within that dock, OnBecameInvisible gets called. Similarly, when the window becomes visible again, of course, OnBecameVisible is called.

using UnityEditor;
using UnityEngine;


public class OnBecameVisibleEditorWindow : EditorWindow
{
	private void OnBecameVisible()
	{
		Debug.Log("Window is visible");
	}

	private void OnBecameInvisible()
	{
		Debug.Log("Window is invisible");
	}


	[MenuItem("Tools/OnBecameVisible Window")]
	public static void Init_ToolsMenu()
	{
		EditorWindow window = EditorWindow.GetWindow<OnBecameVisibleEditorWindow>();
		window.title = "OnBecameVisible";
		window.Show();
	}
}

There is a little gotcha, though. It seems that both methods get called when docking and undocking the window. Not sure why, really. It’s just another Unity quirk!

EditorWindow Inspector Lock Icon

The Inspector and Project windows both have lock icons beside their context menu buttons, like this:

unitytips_showbutton01

I’m going to demonstrate how to make one for your own EditorWindow. Simply implement the method ShowButton(Rect rect), and handle a button in there. Some code:

using UnityEditor;
using UnityEngine;


public class ShowButtonEditorWindow : EditorWindow
{
	private GUIStyle m_IconStyle = new GUIStyle();


	private void OnEnable()
	{
		//NOTE: this is the little pac man from the game view tab
		Texture2D icon = EditorGUIUtility.FindTexture("UnityEditor.GameView");
		m_IconStyle.normal.background = icon;
	}

	private void ShowButton(Rect rect)
	{
		if (GUI.Button(rect, GUIContent.none, m_IconStyle))
		{
			Debug.Log("Icon clicked: " + rect);
		}

		//NOTE: you /could/ add extra buttons like this, but
		//		unity doesn't really make room for that, so
		//		there will be some anomalies
		//rect.x -= 16.0f;

		//if (GUI.Button(rect, GUIContent.none, m_IconStyle))
		//{
		//	Debug.Log("Icon clicked");
		//}
	}


	[MenuItem("Tools/Show Button Window")]
	public static void Init_ToolsMenu()
	{
		EditorWindow window = EditorWindow.GetWindow<ShowButtonEditorWindow>();
		window.title = "Show Button";
		window.Show();
	}
}

This will create a button that has the little Pac Man icon from the Game View window that, when clicked, simply spits out a debug log message.

unitytips_showbutton02

The code that goes inside the body of ShowButton is completely up to you. It’s probably best to stick to the GUI functions and avoid all of the layout stuff.

I’d like to add some notes here about the Rect that is passed to ShowButton. It is created by the Unity Editor’s internal code, and is intended to be used as-is without modification. The x and y are automatically set for you and the dimensions are 16×16. You could technically change the values of the Rect to whatever you want. It’s not really advantageous to do so since it’s possible that the button will not fit correctly into the titlebar. The example code I’ve listed has a commented out section that demonstrates how to make a second button. If you are curious, uncomment that section and open the window. Resize the window to its smallest. Notice the overlap?

unitytips_showbutton03

I can’t recommend doing this, especially if you’re making a tool that will go on the Asset Store. It’s your window, however, so if you need this for whatever reason, it’s available.

EditorWindow Custom Context Menu

Context-clicking an EditorWindow tab shows the context menu for that window. The standard context menu has the items: Maximize, Close Tab, and Add Tab. Some of the built-in windows have extra items in their context menus, such as the Inspector which also has: Normal, Debug, and Lock.

unitytips_custommenu03

I’m going to show you how to add your own custom menu items to your window’s context menu. It’s really simple. You just have to implement the IHasCustomMenu interface like so.

using UnityEditor;
using UnityEngine;


public class CustomMenuEditorWindow : EditorWindow, IHasCustomMenu
{
	private GUIContent m_MenuItem1 = new GUIContent("Menu Item 1");
	private GUIContent m_MenuItem2 = new GUIContent("Menu Item 2");

	private bool m_Item2On = false;


	//Implement IHasCustomMenu.AddItemsToMenu
	public void AddItemsToMenu(GenericMenu menu)
	{
		menu.AddItem(m_MenuItem1, false, MenuItem1Selected);
		menu.AddItem(m_MenuItem2, m_Item2On, MenuItem2Selected);

		//NOTE: do not show the menu after adding items,
		//		Unity will do that after adding the default
		//		items: maximize, close tab, add tab >
	}

	private void MenuItem1Selected()
	{
		Debug.Log("Menu Item 1 selected");
	}

	private void MenuItem2Selected()
	{
		m_Item2On = !m_Item2On;

		Debug.Log("Menu Item 2 is " + m_Item2On);
	}


	[MenuItem("Tools/Custom Menu Window")]
	public static void Init_ToolsMenu()
	{
		EditorWindow window = EditorWindow.GetWindow<CustomMenuEditorWindow>();
		window.title = "Custom Menu";
		window.Show();
	}
}

 

The method of note is AddItemsToMenu(GenericMenu menu). Inside the method body, add the menu items you need in order from top to bottom. If you need to, take a moment to read up on GenericMenu.

Here are the results of the code above.

unitytips_custommenu01  unitytips_custommenu02

Context-clicking the window’s tab will show the menu as will clicking the little icon on the right over there. If an item should be checked, just pass a value of true to menu.AddItem() as the on parameter. See Menu Item 2 in the example.

EditorWindow Maximize/Unmaximize

Unity does a weird thing with docked windows when using Maximize, then Unmaximize (uncheck Maximize). When a docked window is maximized, either through the context menu or the space bar, Unity calls OnFocus and OnLostFocus a few times in a row ending with the window focused. When the window is restored, a new instance of the window gets created but never actually becomes visible.

Using Debug logs, what I’m seeing is this sequence

  1. OnEnable on new window
  2. OnFocus on new window
  3. OnLostFocus on old window
  4. OnLostFocus on new window
  5. OnFocus on old window
  6. OnFocus on old window again
  7. OnDisable on new window
  8. OnDestroy on new window
  9. OnFocus on old window

As far as I can tell, the UnityEditor.WindowLayout.Unmaximize() function is the culprit. It’s loading some kind of serialized copy of the window stored as an editor layout. I guess it was created when the window was maximized in order to preserve the window’s dimensions and docked position. Then, when it restores from the layout, it has to deserialize it from the layout file which essentially makes a new instance. It then has to destroy that new instance.

You might be asking, so what? Well, if you have a window that tries to preserve its data by using ScriptableObjects for when the window gets closed and reopened, it matters. Because when the new window instance is created in the background it “steals” the data references and then silently kills them by orphaning those references. So, the old window, the one you are left with has a bunch of dead ScriptableObject references that don’t actually point to your data.

I ran into this with JumpTo. Gonna have to refactor a good bit to work around it.