Hoje eu tive um pequeno problema com o Unity:
eu tinha um componente simples que muda meu renderizador sortingLayerName
baseado em alguma condição. Algo assim:
class MyComp : Monobehavior {
void Update() {
if (somecondition) {
renderer.sortingLayerName = "Some";
} else {
renderer.sortingLayerName = "Another";
}
}
}
Realmente, eu tive que lidar com esse tipo de script todos os dias para Oh! Estou ficando mais alto .
Por exemplo, para ajustar a ordem de classificação do jogador quando está dentro ou fora de um edifício. Ou a posição do item quando é agarrado pelo jogador. É muito comum em jogos 2D manipular as ordens de classificação e odeio escrever a string exata da camada de classificação manualmente. Se eu cometer um erro de digitação no nome da camada, ele será interrompido silenciosamente. É muito irritante.
Então eu refatorei assim:
Em primeiro lugar, movi os nomes das duas camadas em duas variáveis públicas, para poder alterná-los em meu editor. Ainda é muito inconveniente.
Eu poderia criar um Editor
script específico para personalizá-lo, mas é muito chato escrever um componente para esse tipo de problema. Eu quero algo mais genérico …
Em primeiro lugar, defini um novo System.Attribute
como este:
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; }
}
Eu usei para decorar meu componente:
[HasSortingLayerName('ActiveLayerName', 'InactiveLayerName')]
class MyComp : Monobehavior {
public string ActiveLayerName;
public string InactiveLayerName;
void Update() {
if (somecondition) {
renderer.sortingLayerName = "Some";
} else {
renderer.sortingLayerName = "Another";
}
}
}
Finalmente, defini um CustomEditor
script genérico para a MonoBehavior
(= todos os tipos de 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;
}
}
E TA-DA!
Como funciona
O GetSortingLayerNames
método extrai a lista de nomes de camadas de classificação.
No OnEnable
método, verifico se o componente atual possui um Attribute
do tipo HasSortingLayerName
. Se sim, ele extrai um SerializableProperty
by Names
. Finalmente, ele usa um EditorGUILayout.Popup
para mostrar o pop-up no editor!
Muito simples, mas muito útil: apenas definir o HasSortingLayerName
atributo em um componente, faz com que o editor desenhe um pop-up para escolher a camada de classificação!
Respostas relacionadas:
PlayerPrefs aprimorados para Unity