Una de las claves para aumentar la velocidad de creación de juegos es crear editores personalizados para los componentes más utilizados. Para el ejemplo, usaremos este script muy simple que siempre mantiene un objeto mirando a un punto. Añade este script a tu proyecto, y colócalo en un gameobject cubo en tu escena. El script debe llamarse «LookAtPoint»
//C# Example (LookAtPoint.cs)using UnityEngine;public class LookAtPoint : MonoBehaviour{ public Vector3 lookAtPoint = Vector3.zero; void Update() { transform.LookAt(lookAtPoint); }}
//JS Example (LookAtPoint.js)#pragma strictvar lookAtPoint = Vector3.zero;function Update(){ transform.LookAt(lookAtPoint);}
Esto mantendrá un objeto orientado hacia un punto del espacio-mundo. Actualmente este script sólo se activa en el modo de juego, es decir, cuando el juego se está ejecutando. Cuando se escriben scripts para el editor, a menudo es útil hacer que ciertos scripts se ejecuten también durante el modo de edición, mientras el juego no se está ejecutando. Puedes hacer esto añadiendo un atributo ExecuteInEditMode al mismo:
//C# Example (LookAtPoint.cs)using UnityEngine;public class LookAtPoint : MonoBehaviour{ public Vector3 lookAtPoint = Vector3.zero; void Update() { transform.LookAt(lookAtPoint); }}
//JS Example (LookAtPoint.js)#pragma strict@script ExecuteInEditMode()var lookAtPoint = Vector3.zero;function Update(){ transform.LookAt(lookAtPoint);}
Ahora, si mueves el objeto que tiene este script en el editor, o cambias los valores de «Look At Point» en el inspector -incluso cuando no está en modo de juego- el objeto actualizará su orientación correspondientemente para que siga mirando al punto objetivo en el espacio del mundo.
Creando un Editor Personalizado
Lo anterior demuestra cómo se pueden obtener scripts simples que se ejecutan durante el tiempo de edición, sin embargo esto por sí solo no le permite crear sus propias herramientas de edición. El siguiente paso es crear un Editor Personalizado para el script que acabamos de crear.
Cuando usted crea un script en Unity, por defecto hereda de MonoBehaviour, y por lo tanto es un Componente que puede ser colocado en un objeto de juego. Cuando se coloca en un objeto de juego, el Inspector muestra una interfaz por defecto para ver y editar todas las variables públicas que se pueden mostrar – como enteros, flotadores, cadenas, Vector3’s, etc.
Así es como se ve el inspector por defecto para nuestro script de arriba:
Un editor personalizado es un script separado que reemplaza este diseño por defecto con cualquier control del editor que usted elija.
Para comenzar a crear el editor personalizado para nuestro script LookAtPoint, debe crear otro script con el mismo nombre, pero con «Editor» anexado. Así que para nuestro ejemplo: «LookAtPointEditor».
//c# Example (LookAtPointEditor.cs)using UnityEngine;using UnityEditor;public class LookAtPointEditor : Editor { SerializedProperty lookAtPoint; void OnEnable() { lookAtPoint = serializedObject.FindProperty("lookAtPoint"); } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(lookAtPoint); serializedObject.ApplyModifiedProperties(); }}
//JS Example (LookAtPointEditor.js)#pragma strict@CustomEditor(LookAtPoint)@CanEditMultipleObjectsclass LookAtPointEditor extends Editor { var lookAtPoint : SerializedProperty; function OnEnable() { lookAtPoint = serializedObject.FindProperty("lookAtPoint"); } function OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(lookAtPoint); serializedObject.ApplyModifiedProperties(); }}
Esta clase tiene que derivar de Editor. El atributo CustomEditor informa a Unity de qué componente debe actuar como editor. El atributo CanEditMultipleObjects le dice a Unity que puede seleccionar múltiples objetos con este editor y cambiarlos todos al mismo tiempo.
El código en OnInspectorGUI se ejecuta cada vez que Unity muestra el editor en el Inspector. Usted puede poner cualquier código GUI aquí – funciona igual que OnGUI hace para los juegos, pero se ejecuta dentro del Inspector. El editor define la propiedad objetivo que usted puede utilizar para acceder al objeto que está siendo inspeccionado. Este es el aspecto de nuestro inspector personalizado:
No es muy interesante porque todo lo que hemos hecho hasta ahora es recrear el campo Vector3, exactamente como nos muestra el inspector por defecto, por lo que el resultado es muy similar (aunque el campo «Script» ahora no está presente, porque no hemos añadido ningún código del inspector para mostrarlo).
Sin embargo, ahora que tienes el control sobre cómo se muestra el inspector en un script del Editor, puedes usar el código que quieras para disponer los campos del inspector, permitir al usuario ajustar los valores, e incluso mostrar gráficos u otros elementos visuales. De hecho todos los inspectores que usted ve dentro del Editor de Unity incluyendo los inspectores más complejos como el sistema de terreno y los ajustes de importación de animación, son todos hechos usando el mismo API que usted tiene acceso cuando crea sus propios Editores personalizados.
Aquí hay un ejemplo simple que extiende su script del editor para mostrar un mensaje indicando si el punto objetivo está por encima o por debajo del gameobject:
//c# Example (LookAtPointEditor.cs)using UnityEngine;using UnityEditor;public class LookAtPointEditor : Editor{ SerializedProperty lookAtPoint; void OnEnable() { lookAtPoint = serializedObject.FindProperty("lookAtPoint"); } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(lookAtPoint); serializedObject.ApplyModifiedProperties(); if (lookAtPoint.vector3Value.y > (target as LookAtPoint).transform.position.y) { EditorGUILayout.LabelField("(Above this object)"); } if (lookAtPoint.vector3Value.y < (target as LookAtPoint).transform.position.y) { EditorGUILayout.LabelField("(Below this object)"); } }}
//JS Example (LookAtPointEditor.js)#pragma strict@CustomEditor(LookAtPoint)@CanEditMultipleObjectsclass LookAtPointEditor extends Editor { var lookAtPoint : SerializedProperty; function OnEnable() { lookAtPoint = serializedObject.FindProperty("lookAtPoint"); } function OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(lookAtPoint); serializedObject.ApplyModifiedProperties(); if (lookAtPoint.vector3Value.y > (target as LookAtPoint).transform.position.y) { EditorGUILayout.LabelField("(Above this object)"); } if (lookAtPoint.vector3Value.y < (target as LookAtPoint).transform.position.y) { EditorGUILayout.LabelField("(Below this object)"); } }}
Así que ahora tenemos un nuevo elemento a nuestro inspector que imprime un mensaje mostrando si el punto objetivo está por encima o por debajo del gameobject.
Esto es sólo rascar la superficie de lo que puedes hacer con el scripting del Editor. Usted tiene acceso completo a todos los comandos IMGUI para dibujar cualquier tipo de interfaz, incluyendo la representación de escenas utilizando una cámara dentro de las ventanas del editor.
Adiciones de la Vista de Escena
Puede añadir código adicional a la Vista de Escena mediante la implementación de un OnSceneGUI en su editor personalizado.
OnSceneGUI funciona igual que OnInspectorGUI – excepto que se ejecuta en la vista de escena. Para ayudarte a hacer tus propios controles de edición en la vista de escena, puedes utilizar las funciones definidas en la clase Handles. Todas las funciones allí están diseñadas para trabajar en vistas de escena 3D.
//C# Example (LookAtPointEditor.cs)using UnityEngine;using UnityEditor;public class LookAtPointEditor : Editor{ SerializedProperty lookAtPoint; void OnEnable() { lookAtPoint = serializedObject.FindProperty("lookAtPoint"); } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(lookAtPoint); if (lookAtPoint.vector3Value.y > (target as LookAtPoint).transform.position.y) { EditorGUILayout.LabelField("(Above this object)"); } if (lookAtPoint.vector3Value.y < (target as LookAtPoint).transform.position.y) { EditorGUILayout.LabelField("(Below this object)"); } serializedObject.ApplyModifiedProperties(); } public void OnSceneGUI() { var t = (target as LookAtPoint); EditorGUI.BeginChangeCheck(); Vector3 pos = Handles.PositionHandle(t.lookAtPoint, Quaternion.identity); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(target, "Move point"); t.lookAtPoint = pos; t.Update(); } }}
//JS Example (LookAtPointEditor.js)#pragma strict@CustomEditor(LookAtPointJS)@CanEditMultipleObjectsclass LookAtPointEditorJS extends Editor { var lookAtPoint : SerializedProperty; function OnEnable() { lookAtPoint = serializedObject.FindProperty("lookAtPoint"); } function OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(lookAtPoint); serializedObject.ApplyModifiedProperties(); if (lookAtPoint.vector3Value.y > (target as LookAtPointJS).transform.position.y) { EditorGUILayout.LabelField("(Above this object)"); } if (lookAtPoint.vector3Value.y < (target as LookAtPointJS).transform.position.y) { EditorGUILayout.LabelField("(Below this object)"); } } function OnSceneGUI() { var t : LookAtPointJS = (target as LookAtPointJS); EditorGUI.BeginChangeCheck(); var pos = Handles.PositionHandle(t.lookAtPoint, Quaternion.identity); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(target, "Move point"); t.lookAtPoint = pos; t.Update(); } }}
Si quieres poner objetos GUI 2D (GUI, EditorGUI y amigos), necesitas envolverlos en llamadas a Handles.BeginGUI() y Handles.EndGUI().