A trick to show sorting layer names popup selector for your component
Today I had a little issue with Unity:
I had a simple component that switches my renderer sortingLayerName
based on some condition. Something like this:
class MyComp : Monobehavior {
void Update() {
if (somecondition) {
renderer.sortingLayerName = "Some";
} else {
renderer.sortingLayerName = "Another";
}
}
}
Really, I had to deal with this kind of scripts every day for Oh! I'm Getting Taller.
For example to adjust the player sorting order when it's inside or outside a building. Or the item position when it's grabbed by the player. It's very common in 2d games to manipulate sorting orders, and I hate to write the exact string of the sorting layer manually. If I make a typo in the layer name, it breaks silently. It's very annoying.
So I refactored it like this:
First of all, I moved the two layer names in two public variable, so I can switch them from my editor. It's still pretty inconvenient.
I could create a specific Editor
script to customize it, but it's pretty annoying to write a component for that kind of problem. I want something more generic...
First of all I defined a new System.Attribute
like this:
using System;
[AttributeUsage(AttributeTargets.Class,Inherited = true)]
public class HasSortingLayer : Attribute
{
string[] _names;
public string[] Names { get { return _names; } }
public HasSortingLayer(params string[] names) { _names = names; }
}
I used it to decorate my component:
[HasSortingLayerName('ActiveLayerName', 'InactiveLayerName')]
class MyComp : Monobehavior {
public string ActiveLayerName;
public string InactiveLayerName;
void Update() {
if (somecondition) {
renderer.sortingLayerName = "Some";
} else {
renderer.sortingLayerName = "Another";
}
}
}
Finally I defined a generic CustomEditor
script for a MonoBehavior
(= all kind of scripts):
using UnityEditor;
using UnityEngine;
using System;
using System.Reflection;
using System.Linq;
using UnityEditorInternal;
[CustomEditor(typeof(MonoBehaviour), true)]
public class SortingLayerEditor : Editor {
SerializedProperty[] properties;
string[] sortingLayerNames;
void OnEnable(){
if (Attribute.IsDefined (target.GetType (), typeof(HasSortingLayerName))) {
var sortingLayer = (HasSortingLayerName)Attribute.GetCustomAttribute(target.GetType (),typeof(HasSortingLayerName));
properties = sortingLayer.Names.Select (s => {
return serializedObject.FindProperty (s);
}).ToArray ();
sortingLayerNames = GetSortingLayerNames ();
}
}
public override void OnInspectorGUI() {
base.OnInspectorGUI ();
if (properties != null && sortingLayerNames != null) {
foreach (var p in properties) {
if (p == null) {
continue;
}
int index = Mathf.Max (0, Array.IndexOf (sortingLayerNames, p.stringValue));
index = EditorGUILayout.Popup (p.displayName, index, sortingLayerNames);
p.stringValue = sortingLayerNames [index];
}
if (GUI.changed) {
serializedObject.ApplyModifiedProperties ();
}
}
}
public string[] GetSortingLayerNames() {
Type internalEditorUtilityType = typeof(InternalEditorUtility);
PropertyInfo sortingLayersProperty = internalEditorUtilityType.GetProperty("sortingLayerNames", BindingFlags.Static | BindingFlags.NonPublic);
var sortingLayers = (string[])sortingLayersProperty.GetValue(null, new object[0]);
return sortingLayers;
}
}
And TA-DA!
How it works
The GetSortingLayerNames
method extracts the list of sorting layer names.
In the OnEnable
method I check if the current component has an Attribute
of type HasSortingLayerName
. If so, it extracts a SerializableProperty
by Names
. Finally, it uses a EditorGUILayout.Popup
to show the popup in the editor!
Pretty simple, but very useful: Just setting the HasSortingLayerName
attribute on a Component, makes the editor drawing a popup to choose the sorting layer!