Last Updated: April 20, 2021
·
8.881K
· ProGM

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!
Example

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!

Related protips:

Improved PlayerPrefs for Unity