Hvordan legge til dine egne verktøy til Unity er Editor

How legge til dine egne verktøy til Unity Redigerings
16
Del
20
Del

Denne Cyber ​​Monday Envato Tuts + kurs vil bli redusert til bare $ 3. Ikke gå glipp av.

I denne opplæringen vil du lære hvordan du kan forlenge Unity3D redaktør slik at du kan gjøre bedre bruk av den i prosjektet. Du vil lære å tegne din egen dings, opprette og slette objekter i kode, opprette redaktør vinduer, bruke komponenter, og tillater brukeren å angre en handling de tar med skriptet.


Denne opplæringen forutsetter at du allerede vet det grunnleggende Unity arbeidsflyt. Hvis du vet hvordan du oppretter objekter, prefabs, scener, flytte rundt i editoren, feste komponenter så er du god til å gå!



Endelig resultat Forhåndsvisning

La oss ta en titt på det endelige resultatet vi skal jobbe mot:

Som du ser, vi skal lage en redigeringsvinduet, og en fargevelger der utvalget vi skal bruke til å tegne et rutenett. Vi vil også være i stand til å opprette og slette objekter, glefset til dette rutenettet, og angre slike handlinger



Trinn 1:. Gizmoer

Først vil vi lære å bruke dingser . Her er noen eksempler på innebygde dingser

Dette er den du vil sannsynligvis se mest i Unity, siden det er trukket for hvert objekt som har en Transform komponent knyttet til den -. Så i utgangspunktet hver valgt objektet vil ha dette gizmo trukket

Her er en annen dings, som gjør oss i stand til å se størrelsen på BoxCollider knyttet til spillet vårt objekt



Trinn 2:.. Lag en Grid Script

Lag en C # skript som vi kan bruke til å tegne vår egen dings for et objekt; Vi vil trekke en enkel nettet i editoren som et eksempel
hjelp UnityEngine, bruker System.Collections; public class Grid:. MonoBehaviour {void Start () {} void Update () {}}

For et rutenett vi må legge til to variabler, bredden og høyden
public class Grid:. MonoBehaviour {public float width = 32.0f; public float height = 32.0f; ugyldig Start () {} void Update () {}}

Å trekke i editoren vi trenger å bruke OnDrawGizmos tilbakeringing, så la oss lage det
public class Grid:. MonoBehaviour {public float width = 32.0f; public float height = 32.0f; ugyldig Start () {} void Update () {} void OnDrawGizmos () {}}



Trinn 3: Tegn Grid

Hvis du vil tegne et rutenett vi trenger et sett med horisontale og vertikale linjer og plasseringen av redaktørens kamera så vi vet rundt hvilket punkt bør vi trekke vår rutenett. . Først, la oss redde kameraets posisjon til en egen variabel
void OnDrawGizmos () {Vector3 pos = Camera.current.transform.position;}

Som du ser, kan vi få redaktørens kameraet ved hjelp av kamera .. .current referanse

Nå vi trenger to for looper som vil trekke de horisontale og vertikale linjer
void OnDrawGizmos () {Vector3 pos = Camera.current.transform.position; for (float y = pos.y - 800.0f; y < pos.y + 800.0f; y + = høyde) {Gizmos.DrawLine (ny Vector3 (-1000000.0f, Mathf.Floor (y /høyde) * høyde, 0.0 f), ny Vector3 (1000000.0f, Mathf.Floor (y /høyde) * høyde, 0.0f)); } For (float x = pos.x - 1200.0f; x < pos.x + 1200.0f; x + = bredde) {Gizmos.DrawLine (ny Vector3 (Mathf.Floor (x /bredde) * bredde, -1000000.0f, 0.0f), ny Vector3 (Mathf.Floor (x /bredde) * bredde, 1000000.0f, 0.0f)); }}

For å trekke linjer vi bruker Gizmos.DrawLine (). Merk at gizmoer klasse har mange andre tegne API metoder, så det er mulig å trekke slike primitive som kube eller kule eller selv deres trådmodeller. Du kan også tegne et bilde hvis du må.

Rutenettlinjene bør være uendelig lang, men float.positiveInfinity og float.negativeInfinity ikke synes å fungere godt med å tegne linjene, så vi kan bare sette vilkårlig store tall i stedet for dem. Også antall linjer strengt avhenge konstantene vi satt i for sløyfer definisjoner; teknisk vi ikke bør la de konstanter sånn, men det er bare en test kode

For å se rutenettet, opprette et tomt objekt og legge skriptet til det.



Trinn 4 : Opprett en egendefinert Inspector

Det neste du må dekke er å tilpasse inspektøren. For å gjøre det vi trenger for å lage en redaktør script. Opprett en ny C # fil og gi den navnet GridEditor
. Dette skriptet bør plasseres i Editor
mappe; hvis du ikke har en så skaper det nå
hjelp UnityEngine, bruker UnityEditor, bruker System.Collections [CustomEditor (typeof (Grid))] public class GridEditor. Editor {}

Denne gangen har vi også trenge å bruke UnityEditor å kunne gjøre bruk av tekst klasser og funksjoner. Å overstyre standard inspektør for vår Grid objekt vi må legge til et attributt før vår klasse erklæring, [CustomEditor (typeof (Grid))] lar Unity vet at vi skal tilpasse Grids inspektør. For å kunne bruke redaktøren callbacks, må vi utlede fra redaktøren klassen i stedet for MonoBehaviour

For å endre den nåværende inspektør vi trenger for å overstyre den gamle
public class GridEditor:.. Editor {public styre void OnInspectorGUI () {}}

Hvis du sjekker nettet objektets inspektør i editoren nå, vil det være tom, selv om selve objektet har noen felles medlemmer. Det er fordi ved å overstyre OnInspectorGUI () vi forkastet standard inspektør for å lage en tilpasset en i stedet



Trinn 5:. Bruk GUILayout å fylle Custom Inspector

Før vi skaper noen områder vi trenger å få en referanse til objektet som inspektør gjelder. Vi har faktisk referanse allerede - det heter målet - men for enkelhets skyld vil vi lage en referanse til Grid komponent av dette objektet. Først, la oss erklære det
public class GridEditor. Editor {Grid grid;

Vi bør tildele den i OnEnable () funksjon som kalles så snart Inspektøren er aktivert
public class GridEditor: Editor {. Grid grid; public void OnEnable () {grid = (Grid) målet; }

La oss lage noen inspektør felt nå. Vi vil bruke GUILayout og EditorGUILayout klasser for at
public override void OnInspectorGUI () {GUILayout.BeginHorizontal (.); GUILayout.Label ("Grid bredde"); grid.width = EditorGUILayout.FloatField (grid.width, GUILayout.Width (50)); GUILayout.EndHorizontal ();}

Den første linjen, GUILayout.BeginHorizontal (); viser at vi ønsker å plassere følgende inspektør elementer ved siden av hverandre, fra venstre til høyre. Som du kan forestille deg, den siste linjen, GUILayout.EndHorizontal (); indikerer at vi ikke lenger ønsker å gjøre det. Selve elementene er i mellom disse to linjene. Den første er en enkel etikett (i vårt tilfelle vil det være å vise Grid Bredde
tekst), og deretter ved siden av det vi skaper et EditorGUILayout.FloatField som er som du kan forestille deg en dupp felt. Merk at vi tilordner grid.width til verdien av at FloatField, og duppen feltet selv viser verdien av grid.width. Vi har også satt sin bredde 50 piksler

La oss se om feltet er lagt til inspektøren.



Trinn 6: Fyll Inspector og omlakker Scene

La oss nå legge en mer element til inspektøren; denne gangen vil det bli grid.height
offentlig overstyring void OnInspectorGUI () {GUILayout.BeginHorizontal (.); GUILayout.Label ("Grid bredde"); grid.width = EditorGUILayout.FloatField (grid.width, GUILayout.Width (50)); GUILayout.EndHorizontal (); GUILayout.BeginHorizontal (); GUILayout.Label ("Grid Høyde"); grid.height = EditorGUILayout.FloatField (grid.height, GUILayout.Width (50)); GUILayout.EndHorizontal ();}

Det ville være alt for våre nettobjekt felt, hvis du ønsker å vite om at du kan bruke i inspektøren så kan du besøke de Unity referanse sidene på EditorGUILayout og GUILayout andre felt og elementer .

Legg merke til at de endringene vi gjør i vår nye inspektør er synlig først etter at vi velger Scene Vis vinduet. . Å gjøre dem synlige når de er laget vi kan kalle SceneView.RepaintAll ()
offentlig overstyring void OnInspectorGUI () {GUILayout.BeginHorizontal (); GUILayout.Label ("Grid bredde"); grid.width = EditorGUILayout.FloatField (grid.width, GUILayout.Width (50)); GUILayout.EndHorizontal (); GUILayout.BeginHorizontal (); GUILayout.Label ("Grid Høyde"); grid.height = EditorGUILayout.FloatField (grid.height, GUILayout.Width (50)); GUILayout.EndHorizontal (); SceneView.RepaintAll ();}

Nå har vi ikke trenger å klikke utenfor inspektør for å se resultatene av endringene



Trinn 7: Håndter Editor Input
La oss nå prøve å håndtere redaktørens innspill, akkurat som vi ville gjøre det i spillet. En tast eller mus stater bør være tilgjengelig for oss. Å ha denne funksjonaliteten vi må legge til en onSceneGUIDelegate tilbakeringing til vår SceneView. La oss kalle vår oppdateringsfunksjonen GridUpdate ()
public void OnEnable () {grid = (Grid) målet.; SceneView.onSceneGUIDelegate = GridUpdate;} void GridUpdate (SceneView sceneview) {}

Nå er vi bare trenger å få innspill Hendelses
annullere GridUpdate (SceneView sceneview) {Hendelses e = Event.current;.}


< h2> Trinn 8: Lag en Prefab

For ytterligere å spille med redaktøren skript vi trenger et spill objekt som vi vil være i stand til å bruke. La oss lage en enkel kube, og gjøre et ferdighus ut av det.

Du kan matche størrelsen på rutenettet til kuben eller den andre veien rundt og justere det med et rutenett.

Som du kan se, i hierarkiet visning kube
tekst er farget i blått; Dette betyr at den er koblet til et ferdighus. Du kan se at ferdighus i prosjektvinduet



Trinn 9:. Opprett et objekt fra redaktøren Script

Nå skal vi lage et objekt fra redaktøren manuset. La oss gå tilbake til vår GridEditor.cs Hotell og forlenge GridUpdate () -funksjonen.

La oss lage objektet når nøkkelen en
trykkes.
Ugyldig GridUpdate (SceneView sceneview) {Hendelses e = Event.current; if (e.isKey & & e.character == 'a') {GameObject obj; }}

Som du ser, vi bare sjekke om hendelsen er et sentralt statlig endring og om tegnet som ble trykket er 'a'. Vi skaper også en referanse for vår nye objekt. Nå la oss instantiate det
annullere GridUpdate (SceneView sceneview) {Hendelses e = Event.current.; if (e.isKey & & e.character == 'a') {GameObject obj; if (Selection.activeObject) {obj = (GameObject) instantiate (Selection.activeObject); obj.transform.position = new Vector3 (0.0f, 0.0f, 0.0f); }}}

Selection.activeObject er en referanse til den markerte objektet i editoren. Hvis et objekt er valgt da vi bare klone den og endre den klone posisjon til (0.0, 0.0, 0.0).

La oss teste om det fungerer. Du må være oppmerksom på én ting: vår GridUpdate () slutter å virke når eiendelene er gjeninnført /uthvilt, og for å aktivere det du har å velge objektet (for eksempel fra hierarkiet visning) at redaktøren manuset refererer til - i vårt eksempel er det Grid
objekt. Du må også huske at inngangs arrangementer vil bli fanget bare hvis Scene visningen er valgt



Trinn 10:. Bruke et Prefab fra redaktøren Script

Alt vi klart å klone objektet, det klonede objektets link til ferdighus er ikke-eksisterende.

Som du kan se, Cube (Clone)
navnet vises med vanlig svart skrift og det betyr at det ikke er koblet til den ferdighus som den opprinnelige kuben er. Hvis vi skulle kopiere den opprinnelige kuben manuelt i editoren, ville klonet kuben være knyttet til Cube
ferdighus. For å gjøre det fungere på denne måten for oss at vi må bruke InstantiatePrefab () -funksjonen fra EditorUtility klasse.

Før vi bruke denne funksjonen vi trenger for å få det valgte objektets ferdighus. For å gjøre det må vi bruke GetPrefabParent () som også tilhører EditorUtility klassen
annullere GridUpdate (SceneView sceneview) {Hendelses e = Event.current.; if (e.isKey & & e.character == 'a') {GameObject obj; Objekt ferdighus = EditorUtility.GetPrefabParent (Selection.activeObject); if (ferdighus) {

Vi kan også stoppe å sjekke om Selection.activeObject eksisterer, fordi hvis det ikke da ferdighus vil være lik null, og derfor kan vi komme unna med å sjekke bare prefab referanse.

La oss nå instantiate vår prefab og sette sin posisjon
annullere GridUpdate (SceneView sceneview) {Hendelses e = Event.current.; if (e.isKey & & e.character == 'a') {GameObject obj; Objekt ferdighus = EditorUtility.GetPrefabParent (Selection.activeObject); if (ferdighus) {obj = (GameObject) EditorUtility.InstantiatePrefab (ferdighus); obj.transform.position = new Vector3 (0.0f, 0.0f, 0.0f); }}}

Og det er det - la oss sjekke om det klonede kuben er knyttet til ferdighus nå



Trinn 11: Trans Screen Mouse coords World coords

The Event. klasse ikke la oss få vite hvor musen er i verden, det tilbyr bare skjermplass muse koordinater. Her er hvordan vi konvertere dem slik at vi kan få en tilnærmet verden plass mus posisjon
annullere GridUpdate (SceneView sceneview) {Hendelses e = Event.current.; Ray r = Camera.current.ScreenPointToRay (ny Vector3 (e.mousePosition.x, -e.mousePosition.y + Camera.current.pixelHeight)); Vector3 mousePos = r.origin;

Først bruker vi redaktørens kamera ScreenPointToRay å få ray fra skjermkoordinater, men dessverre før at vi trenger å oversette hendelsens plass på skjermen til en plass som er akseptabelt for ScreenPointToRay ().

e.mousePosition holder musen posisjon i et koordinatsystem plass hvor øvre venstre hjørnet er (0, 0) poeng og nederst i høyre hjørne er lik (Camera.current.pixelWidth, -Camera.current.pixelHeight ). Vi trenger å oversette den til plassen der nederst
venstre hjørnet er (0, 0) og øverst til høyre er (Camera.current.pixelWidth, Camera.current.pixelHeight), som er ganske enkel.

Det neste vi skal gjøre er å redde ray opprinnelse til vår mousePos vektor, så det er lett tilgjengelig.

Nå kan vi tildele klone stilling til hvor musen er.
hvis (ferdighus) {obj = (GameObject) EditorUtility.InstantiatePrefab (ferdighus); obj.transform.position = new Vector3 (mousePos.x, mousePos.y, 0.0f);}

Legg merke til at når kameraet er stilt helt flatt da tilnærming av musen posisjon på den ene aksen er virkelig virkelig ille, det er derfor jeg setter z posisjon klone manuelt. Nå kuber bør opprettes hvor musen er



Trinn 12:. Juster Cubes til Grid

Siden vi har fått vår grid satt opp, det ville være en skam ikke å bruke det; la oss bruke vår muse posisjon til å justere opprettet kuber til nettet
if (ferdighus) {obj = (GameObject) EditorUtility.InstantiatePrefab (ferdighus).; Vector3 justert = new Vector3 (Mathf.Floor (mousePos.x /grid.width) * grid.width + grid.width /2.0F, Mathf.Floor (mousePos.y /grid.height) * grid.height + grid.height /2.0f, 0.0f); obj.transform.position = justert;}

Ta en titt på resultatet:



Trinn 13: ødelegge et objekt fra redaktøren Script

I dette trinnet vi vil slette objekter programma i editoren. Vi kan gjøre det ved hjelp DestroyImmediate (). I dette eksempelet la oss gjøre en større bruk av markerings klasse og slette alle de valgte objektene når 'd' tasten trykkes
if (e.isKey & & e.character == 'a'). { GameObject obj; Objekt ferdighus = EditorUtility.GetPrefabParent (Selection.activeObject); if (ferdighus) {obj = (GameObject) EditorUtility.InstantiatePrefab (ferdighus); Vector3 justert = new Vector3 (Mathf.Floor (mousePos.x /grid.width) * grid.width + grid.width /2.0F, Mathf.Floor (mousePos.y /grid.height) * grid.height + grid.height /2.0f, 0.0f); obj.transform.position = justert; }} else if (e.isKey & & e.character == 'd') {foreach (GameObject obj i Selection.gameObjects) DestroyImmediate (obj);}

Når 'd' tasten trykkes vi kjører gjennom alle de valgte objektene og slette hver enkelt av dem. Selvfølgelig kan vi også trykke Slett
nøkkelen i redigeringsprogram til å slette disse objektene, men da disse ikke ville bli fjernet av vår script. Test det i redigereren



Trinn 14:. Angre objektforekomst

I dette trinnet vil vi gjøre bruk av angre klasse, som i utgangspunktet lar oss angre hver handling som vår redaktør script gjør. La oss starte med å løsne objektet skapelsen.

For å være i stand til å ødelegge et objekt som vi opprettet i editor må vi kalle Undo.RegisterCreatedObjectUndo (). Det tar to argumenter: den første er objektet som har blitt skapt og den andre er navnet på angre. Navnet på handlingen som foregår alltid vises under til angres Rediger- >. Angre navn
if (ferdighus) {obj = (GameObject) EditorUtility.InstantiatePrefab (ferdighus); Vector3 justert = new Vector3 (Mathf.Floor (mousePos.x /grid.width) * grid.width + grid.width /2.0F, Mathf.Floor (mousePos.y /grid.height) * grid.height + grid.height /2.0f, 0.0f); obj.transform.position = justert; Undo.RegisterCreatedObjectUndo (obj, "Create" + obj.name); }

Hvis du lager et par terninger som bruker en
nøkkel og deretter prøve å angre nå vil du legge merke til at alle de opprettede kuber har blitt slettet. Det er fordi alle disse opprettet kuber gikk inn i en enkelt angre hendelse



Trinn 15:. Angre Enkeltobjektforekomst

Hvis vi ønsker å plassere hver opprettet objekt på en annen angre hendelsen og gjøre det mulig å angre skape dem én etter én må vi bruke Undo.IncrementCurrentEventIndex ()
if (ferdighus) {Undo.IncrementCurrentEventIndex (.); obj = (GameObject) EditorUtility.InstantiatePrefab (ferdighus); Vector3 justert = new Vector3 (Mathf.Floor (mousePos.x /grid.width) * grid.width + grid.width /2.0F, Mathf.Floor (mousePos.y /grid.height) * grid.height + grid.height /2.0f, 0.0f); obj.transform.position = justert; Undo.RegisterCreatedObjectUndo (obj, "Create" + obj.name); }

Hvis du tester skriptet nå vil du se at kubene slettes en etter en ved å løsne deres skapelse



Trinn 16:. Angre Object Slette

Hvis du vil angre objektet sletting vi må bruke Undo.RegisterSceneUndo (). Det er en veldig treg funksjon som i hovedsak sparer scenen staten slik at vi senere kan gå tilbake til den tilstanden ved å utføre en angre handling. Dessverre ser det ut til å være den eneste måten for nå å få slettet gjenstander tilbake på scenen
else if (e.isKey & & e.character == 'd'). {Undo.IncrementCurrentEventIndex (); Undo.RegisterSceneUndo ("Slett valgte objekter"); foreach (GameObject obj i Selection.gameObjects) DestroyImmediate (obj);}

Undo.RegisterSceneUndo () tar bare ett argument, og det er angre navn. Etter sletting et par terninger med d
tasten kan du angre på at slettingen



Trinn 17:. Lag redaktør Window Script

Opprett et nytt manus, og la oss gjøre dette til en forlenge EditorWindow istedenfor Editor. La oss kalle det GridWindow.cs
hjelp UnityEngine, bruker UnityEditor, bruker System.Collections; public class GridWindow. EditorWindow {public void init () {}}

La oss lage en referanse til vår Grid
objekt slik at vi får tilgang til den fra vinduet
public class GridWindow: EditorWindow {Grid grid;. public void Init () {grid = (Grid) FindObjectOfType (typeof (Grid)); }}

Nå må vi opprette vinduet, vi kan gjøre det fra vår GridEditor
script



Trinn 18:. Opprett GridWindow

I vår OnInspectorGUI () la oss legge til en knapp som vil skape GridWindow
public override void OnInspectorGUI () {GUILayout.BeginHorizontal (.); GUILayout.Label ("Grid bredde"); grid.width = EditorGUILayout.FloatField (grid.width, GUILayout.Width (50)); GUILayout.EndHorizontal (); GUILayout.BeginHorizontal (); GUILayout.Label ("Grid Høyde"); grid.height = EditorGUILayout.FloatField (grid.height, GUILayout.Width (50)); GUILayout.EndHorizontal (); if (GUILayout.Button ("Open Grid Window", GUILayout.Width (255))) {GridWindow vindu = (GridWindow) EditorWindow.GetWindow (typeof (GridWindow)); window.Init (); } SceneView.RepaintAll ();}

Vi bruker GUILayout å lage en knapp, har vi også satt knappens navn og bredde. Den GUILayout.Button returnerer sant når du trykker på knappen, hvis det er tilfelle så vi åpner vår GridWindow.

Du kan gå tilbake til redaktøren og trykk på knappen i vår Grid objekt inspektør.

Når du gjør det, GridWindow
skal dukke opp



Trinn 19:. Lag et fargefelt i GridWindow

Før vi redigere alt fra vinduet vårt ., la oss legge til et fargefelt i vår Grid klasse, så vi kan redigere den senere
public class Grid: MonoBehaviour {public float width = 32.0f; public float height = 32.0f; offentlig Color color = Color.white;.

Nå tildele Gizmos.color i OnDrawGizmos () -funksjonen
void OnDrawGizmos () {Vector3 pos = Camera.current.transform.position; Gizmos.color = farge;

Og nå la oss gå tilbake til GridWindow
manus og lage et fargefelt der, så vi kan velge farge i vinduet. Vi kan gjøre det i OnGUI () tilbakeringing
public class GridWindow. EditorWindow {Grid grid; public void Init () {grid = (Grid) FindObjectOfType (typeof (Grid)); } Void OnGUI () {grid.color = EditorGUILayout.ColorField (grid.color, GUILayout.Width (200)); }}

Greit, nå kan du sjekke om alt fungerer som det skal i editoren:



Trinn 20: Legg en delegat

Akkurat nå setter vi en delegat for å få inngangs hendelser fra scenen visningen vi bruker = tegnet, som ikke er en god metode for å gjøre det fordi det overstyrer alle andre tilbakeanrop. Vi bør bruke + = tegnet i stedet. La oss gå til vår GridEditor.cs
manus og gjøre denne endringen
public void OnEnable () {grid = (Grid) målet.; SceneView.onSceneGUIDelegate + = GridUpdate;.}

Vi trenger også å opprette en OnDisable () tilbakeringing å fjerne vår GridUpdate (), hvis vi ikke gjør det så vil det hope seg opp og bli kalt flere ganger på en gang
public void OnEnable () {grid = (Grid) målet; SceneView.onSceneGUIDelegate + = GridUpdate;} public void OnDisable () {SceneView.onSceneGUIDelegate - = GridUpdate;}
Konklusjon

Det er det for innledningen til redaktøren scripting. Hvis du ønsker å utvide din knowladge, det er mye å lese om emnet i Unity Script Reference - kan det være lurt å sjekke Resources, AssetDatabase eller FileUtil klasser avhengig av dine behov

Dessverre, noen klasser er. ennå udokumentert og på grunn av det, tilbøyelige til å endre uten å jobbe. For eksempel SceneView klasse og dens funksjoner eller Undo.IncrementCurrentEventIndex () funksjon fra Angre klassen. Dersom dokumentasjonen ikke gir de svarene du søker, vil du kanskje prøve å søke gjennom UnityAnswers eller Unity Forum.

Takk for din tid! Anmeldelser