Kluczem do zwiększenia szybkości tworzenia gier jest stworzenie custom editors dla powszechnie używanych komponentów. Dla przykładu, użyjemy tego bardzo prostego skryptu, który zawsze utrzymuje obiekt patrzący na punkt. Dodaj ten skrypt do swojego projektu i umieść go na gameobjectie cube w swojej scenie. Skrypt powinien nazywać się „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);}
Będzie on utrzymywał obiekt zorientowany na punkt w przestrzeni świata. Obecnie skrypt ten będzie aktywny tylko w trybie gry, czyli gdy gra jest uruchomiona. Podczas pisania skryptów do edytora często przydaje się, aby pewne skrypty były wykonywane również w trybie edycji, gdy gra nie jest uruchomiona. Możesz to zrobić dodając do niego atrybut ExecuteInEditMode:
//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);}
Teraz, jeśli przesuniesz obiekt, który ma ten skrypt w edytorze, lub zmienisz wartości „Look At Point” w inspektorze – nawet jeśli nie jest w trybie gry – obiekt odpowiednio zmieni swoją orientację, aby nadal patrzył na punkt docelowy w przestrzeni świata.
Utworzenie własnego edytora
Powyżej pokazano, jak można uruchomić proste skrypty podczas edycji, jednak samo to nie pozwala na stworzenie własnych narzędzi edytora. Następnym krokiem jest stworzenie własnego edytora dla skryptu, który właśnie stworzyliśmy.
Gdy tworzysz skrypt w Unity, domyślnie dziedziczy on po MonoBehaviour, a więc jest komponentem, który może być umieszczony na obiekcie gry. Po umieszczeniu na obiekcie gry Inspektor wyświetla domyślny interfejs do przeglądania i edytowania wszystkich zmiennych publicznych, które mogą być wyświetlane – takich jak liczby całkowite, zmiennoprzecinkowe, ciągi, Vector3’s itp.
Oto jak wygląda domyślny inspektor dla naszego powyższego skryptu:
Niestandardowy edytor jest oddzielnym skryptem, który zastępuje ten domyślny układ dowolnymi kontrolkami edytora, które wybierzesz.
Aby rozpocząć tworzenie niestandardowego edytora dla naszego skryptu LookAtPoint, powinieneś utworzyć inny skrypt o tej samej nazwie, ale z dodatkiem „Editor”. Tak więc dla naszego przykładu: „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(); }}
Ta klasa musi wywodzić się z Editora. Atrybut CustomEditor informuje Unity, dla którego komponentu powinna pełnić rolę edytora. Atrybut CanEditMultipleObjects mówi Unity, że możesz wybrać wiele obiektów za pomocą tego edytora i zmienić je wszystkie w tym samym czasie.
Kod w OnInspectorGUI jest wykonywany za każdym razem, gdy Unity wyświetla edytor w Inspektorze. Możesz umieścić tutaj dowolny kod GUI – działa on tak samo jak OnGUI dla gier, ale jest uruchamiany wewnątrz Inspektora. Editor definiuje właściwość docelową, której można użyć, aby uzyskać dostęp do kontrolowanego obiektu. Oto jak wygląda nasz niestandardowy inspektor:
Nie jest to zbyt interesujące, ponieważ wszystko co zrobiliśmy do tej pory to odtworzenie pola Vector3, dokładnie tak jak pokazuje nam domyślny inspektor, więc wynik wygląda bardzo podobnie (chociaż pole „Script” nie jest teraz obecne, ponieważ nie dodaliśmy żadnego kodu inspektora, aby je pokazać).
Jednakże teraz, gdy masz kontrolę nad tym, jak inspektor jest wyświetlany w skrypcie edytora, możesz użyć dowolnego kodu, który chcesz, aby rozłożyć pola inspektora, pozwolić użytkownikowi dostosować wartości, a nawet wyświetlić grafikę lub inne elementy wizualne. W rzeczywistości wszystkie inspektory, które można zobaczyć w edytorze Unity, w tym bardziej złożone inspektory, takie jak system terenu i ustawienia importu animacji, są tworzone przy użyciu tego samego API, do którego masz dostęp podczas tworzenia własnych edytorów.
Oto prosty przykład, który rozszerza skrypt edytora, aby wyświetlić wiadomość wskazującą, czy punkt docelowy znajduje się powyżej czy poniżej obiektu gry:
//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)"); } }}
Więc teraz mamy nowy element do naszego inspektora, który drukuje wiadomość wskazującą, czy punkt docelowy znajduje się powyżej czy poniżej obiektu gry.
To jest tylko zarysowanie powierzchni tego, co można zrobić za pomocą skryptów edytora. Masz pełny dostęp do wszystkich poleceń IMGUI, aby narysować dowolny typ interfejsu, włączając w to renderowanie scen za pomocą kamery w oknach edytora.
Dodatki do widoku sceny
Możesz dodać dodatkowy kod do widoku sceny, implementując OnSceneGUI w swoim niestandardowym edytorze.
OnSceneGUI działa tak samo jak OnInspectorGUI – z tą różnicą, że jest uruchamiane w widoku sceny. Aby ułatwić sobie tworzenie własnych kontrolek edycji w widoku sceny, możesz użyć funkcji zdefiniowanych w klasie Handles. Wszystkie znajdujące się tam funkcje są przeznaczone do pracy w widokach sceny 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(); } }}
Jeśli chcesz umieścić obiekty GUI 2D (GUI, EditorGUI i przyjaciele), musisz zawinąć je w wywołaniach Handles.BeginGUI() i Handles.EndGUI().