[フレーム]
Last Updated: April 20, 2021
·
9.562K
· 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

AltStyle によって変換されたページ (->オリジナル) /