Articles

GetComponent I C #

I denne opplæringen

  • Tilgang til et annet skript fra innsiden av objektet
  • Hvordan GetComponent arbeid?
  • Får mange komponenter
  • Interaksjon mellom objekter.
  • Interaksjon mellom objekter i hierarkiet
  • SendMessage og BroadcastMessage
  • Interaktive funksjoner

Innledning

Et tilbakevendende problem når Man starter Med Unity er hvordan man får tilgang til medlemmene i ett skript fra et annet skript. Mange ville tro at dereferencing fra manuset navnet ville være nok, de raskt innser det ikke er.

når du utvikler et program, lagres variabler i minnet på forskjellige steder. Hvis et objekt kunne se andre objektmedlemmer, ville det være en risiko for å endre dem selv om det ikke var ment. Hvis to objekter holder samme skript med samme variabelnavn, vil kompilatoren ikke kunne finne ut hvilken vi refererer til.

hvis du vil unngå dette problemet, kan ikke hvert objekt i minnet se andre objekter. Det er da nødvendig å fortelle ett objekt hvor variabelen den trenger er i minnet. Hele pekerprinsippet har blitt droppet for lenge siden, og nå bruker vi referanse i stedet (som faktisk bruker pekeren internt).

det finnes ulike måter å få tilgang til variabler, noen er bedre enn andre, og noen skal bare brukes i bestemte situasjoner.

  1. GetComponent, den vanligste, også den som forvirrer mest Først
  2. SendMessage, det kan se lettere å forstå, men det er også mindre effektivt.
  3. Statiske variabler, den enkleste, men også den mest kompliserte å forstå fullt ut først.

Sette opp en scene

Opprett en ny scene, legg til et tomt spillobjekter og gi det navnet «StormTrooper». Lag to skript som heter «Helse» og «Force». Legg til begge skriptene Til StormTrooper.

Åpne begge skriptene i redigeringsprogrammet og legg til disse linjene:

Helse.cs

using UnityEngine;using System.Collections;public class Health : MonoBehaviour{ private int health = 5; private bool hasForce = false; private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { this.hasForce = Force.ReportForce(); if(hasForce == true) { this.health = 10; } } }}

Kraft.cs

using UnityEngine;using System.Collections;public class Force : MonoBehaviour{ private bool force = false; private void Start() { this.force = true; } private void Update() { if (Input.GetKeyDown(KeyCode.P)) { string str = (this.force == true) ? " the": " no"); Debug.Log("I have" + str + " force"); } } public bool ReportForce() { return force; }}

Prøver denne koden vil bare returnere feil «en objektreferanse er nødvendig for å få tilgang til ikke-statiske medlemmer».

Vi må fortelle Helsekomponenten på StormTrooper hvor Er Kraftkomponenten slik at den kan få tilgang til sine medlemmer. Siden våre skript er begge på samme objekt, trenger vi bare å erklære en variabel av typen skript vi ønsker å få tilgang til og bruke GetComponent for å finne skriptet.

Tilgang til et annet skript inne i objektet

Endre Helse med:

using UnityEngine;using System.Collections;public class Health : MonoBehaviour{ private Force forceScript= null; public int health = 5; private bool hasForce = false; private void Start() { this.forceScript= GetComponent<Force>(); } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { this.hasForce = this.forceScript.ReportForce(); if(this.hasForce == true) { this.health = 10; } } }}

med modifikasjonene, kjør spillet, trykk Space og trykk Deretter P, Din StormTrooper har nå 10 av helse (Merk at En StormTrooper ikke har kraften, dette er bare for eksemplet). Du får tilgang til variabler på samme måte som du fikk tilgang til metoder. Bare sørg for at medlemmene du prøver å nå er erklært offentlig, eller du vil få en feil.

hvis medlemmene var private, ville det ikke være mulig å få tilgang til dem. Alt som ikke bør nås fra utenfor klassen / skriptet, bør være privat for å takle innkapslingsprinsippet.

Hvordan Fungerer GetComponent?

først erklærer vi et objekt av typen skript vi vil nå.

private Force forceScript = null;

Force Er vår type og forceScript er bare et navn vi gir for å bruke referansen i skriptet. Vi kan gi det noe navn.

GetComponent-funksjonen vil se inne i objektet (StormTrooper – spillobjektet) for å finne en komponent som svarer til typen vi passerte. Hvis ingen er funnet, returneres en nullreferanse, og vår forcescript-variabel vil ikke gjøre noe. Hvis en komponent av Type Force er funnet, sendes adressen til komponenten til variabelen forceScript, variabelen peker nå På Kraftkomponenten lagret et sted i minnet. For å få tilgang til De offentlige Medlemmene Av Force, trenger vi bare å dereferere skriptvariabelen ved hjelp av dot-operatøren. Det sies at forceScript-variabelen er et håndtak til objektet. Det gir tilgang til alle offentlige medlemmer(og interne).

script.PublicMembers;

Private og beskyttede medlemmer vil forbli utilgjengelige.

Ved Hjelp Av SerializeField-attributtet kan du se variablene i inspector despute være privat. Din forcescript-variabel starter med verdien Av Ingen (Kraft) og blir Til En Kraftreferanse. Hvis du klikker på den, vil den markere målobjektet.

reference_component

så jeg kan cache komponent Med GetComponent?

Ja, og det anbefales å gjøre det for noen regelmessig brukte komponenter.

Når du åpner innebygde komponenter som Rigidbody, Transform, Renderer,…etc, Unity brukes til å tillate «direkte» tilgang som dette:

private void Update(){ this.transform.position += this.transform.forward; this.rigidbody.AddForce(force);}

de fjernet dem alle, men transform siden det er alltid En Transform på et gitt spillobjekt.

GetComponent bør unngås inne Oppdatering så caching anbefales med noen enkle linjer med kode som:

private Transform myTransform = null;private Rigidbody rigidbody = null;private void Start(){ this.myTransform = this.gameObject.GetComponent<Transform>(); this.rigidbody = this.gameObject.GetComponent<Rigidbody>();}private void Update(){ this.myTransform.position += this.myTransform.forward; this.rigidbody.AddForce(force);}

dette vil spare litt beregning.

Får mange komponenter

Hva om Vår StormTrooper har mange Krefter i seg selv (ikke sannsynlig skjønt). Dupliser Kraften to ganger, Så StormTrooper har tre av dem.

Helse blir:

using UnityEngine;using System.Collections;public class Health : MonoBehaviour{ private Force scripts = null; private int health = 5; private void Start() { this.scripts = this.gameObject.GetComponents<Force>(); } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { for (int i = 0; i < this.scripts.Length; i++) { if(this.scripts.ReportForce()) { this.health += 5; } } } }}

Kjør spillet, trykk Space, Vår StormTrooper blir En Jedi. GetComponents ser etter alle komponenter av typen og returnerer en rekke av dem. Dette er greit hvis du ikke målretter mot en bestemt komponent av typen, men mer sannsynlig alle av dem.

Se følgende ville slå på og av lysene:

public class LightFlicker:MonoBehaviour{ private Light lights = null; private float timer = 0.0f private float clockTimer = 0.0f; private void Start(){ this.lights = this.gameObject.GetComponents<Light>(); this.clockTimer = Random.Range(0.5f, 2.0f); } private void Update(){ this.timer += Time.deltaTime; if(this.timer > this.clockTimer){ this.timer = 0.0f; this.clocktimer = Random.Range(0.5f, 2.0f); for(int i = 0; i < this.lights.Length; i++){ lights.enabled = !lights.enabled; } } }}

Interaksjon mellom objekter.

Vi kan også få to forskjellige objekter til å samhandle på en lignende måte. Opprett et nytt objekt som heter «Sith». Fjern de dupliserte komponentene fra StormTrooper slik at bare en er igjen.

using UnityEngine;using System.Collections;public class DarkSide : MonoBehaviour { private const bool force = true; private Force script = null; private void Start () { this.script = GameObject.Find ("StormTrooper").GetComponent<Force>(); } private void Update () { if(Input.GetKeyDown (KeyCode.Space)) { UseForceToControl (); } } private void UseForceToControl() { if(this.script.ReportForce() == false) { Debug.Log ("Do as I say."); } }}

Endre kraftskriptet Til StormTrooper slik at Han ikke har kraften (Sith ville finne dette mistenkelig…), jeg gir deg en anelse om At Det skjer I startskriptet.

Nedenfor er linjen som finner StormTrooper og får Sin Kraftkomponent.

private void Start () { this.script = GameObject.Find ("StormTrooper").GetComponent<Force>();}

GameObject.Finn () finner først objektet passert som parameter og ser etter den nødvendige komponenten inne i den. Det anbefales å utføre disse handlingene I Starten eller på et tidspunkt i spillet der lags ikke vil påvirke spillingen. GameObject.Finn Og GetComponent er dyre funksjoner siden de trenger å iterere gjennom Hele Hierarkiet. Hvis scenen din bare inneholder 10 objekter, vil det gå bra, men hvis det holder tusenvis av dem (en hær av klone), vil du sikkert se en dårlig effekt. Vurder å kalle disse I Start-funksjonen. Merk at Hvis scenen din inneholder Mange Stormtroopere, Kan Uity ikke vite hvilken du mente å bruke og vil returnere den første den finner. Sannsynligvis holder du en referanse til objektet du vil samhandle med, for eksempel en kollisjon tilbakeringing.

Interaksjon mellom objekter i hierarkiet

Vurder et kjøretøy,, når du kjører det, er tegnet festet til det som et barnobjekt. Kjøretøyet vil ha spesiell funksjon hvis føreren har kraften(bedre håndtering, raskere og så videre). Som sjåføren er vedlagt, er han under i hierarkiet.
kjøretøyet kan finne Kraftkomponenten til sjåføren med samme prinsipp vi brukte tidligere (GameObject.Finn), men vi kan få det til å skje raskere ved å indikere til kjøretøyet at sjåføren er i sitt eget hierarki.

private Force script = null;private void Start () { this.script = this.transform.Find ("Sith").GetComponent<DarkSideForce>();}

GameObject byttes for transformasjon. Faktisk tilhører et barnobjekt transformasjonen av foreldrene, så vi må se på transformasjonen for å finne vårt objekt. I eksemplet er objektet rett under anropsobjektet, hvis Sith er inne i cockpitobjektet under bilobjektet, må du gi banen:

private void Start () { this.script = this.transform.Find ("Cockpit/Sith").GetComponent<DarkSideForce>();}

anropsobjektet er Bilen og refereres som dette i anropet. Cockpit er søkt Og Sith er søkt. Hvis banen er feil, Finn returnerer null og du får En Null Referanse Unntak. Du kan unngå det slik:

private void Start () { Transform tr = this.transform.Find ("Cockpit/Sith"); if(tr != null){ this.script = tr.GetComponent<DarkSideForce>(); }else{Debug.LogWarning("No component found");}}

transform.Finn returnerer En Transformering mens GameObject.Finn returnerer Et GameObject.

Enda bedre, kan vi bruke:

private void Start () { this.script = GetComponentInChildren<DarkSideForce>();}

nå våre kjøretøy kan vite at Vår Sith har kraft og bruke spesiell funksjon.
Et problem å vite om, hvis det overordnede objektet inneholder et skript av typen du leter etter, vil det returnere det. Så tenker du har å gjøre med barnet komponent, er du faktisk arbeider med foreldre en.

i dette eksemplet må vi sjekke for hver type driver hvis Det Er En StormTrooper eller En Sith. En bedre tilnærming ville ha vært å bruke arv slik at toppbaseklassen ville holde kraftvariabelen og en enkelt funksjon ville bli brukt til alle typer drivere. Men arv og effektivitet var ikke vårt hovedproblem her.

SendMessage og BroadcastMessage

SendMessage og BroadcastMeassage Er to andre muligheter som kan være enklere, men også dyrere. Prinsippet forblir det samme, vi har en referanse til et objekt, og vi vil endre et skript det inneholder.

Med SendMessage kan du ringe en funksjon på et objekt uten å måtte finne en referanse til skriptet. Nå forteller du meg: «Hva er all denne forvirringsturen hvis du kan gjøre det enkelt?»

SendMessage bruker refleksjon og er ekstremt treg i forhold Til GetComponent og bør bare vurderes i tilfeller der det ikke vil påvirke spillet eller hvis du ikke har noen annen løsning.

og det burde høres logisk, Med GetComponent angir vi nøyaktig hvor skriptet er plassert i minnet. Med SendMessage kjenner kompilatoren objektet, men det vil gå gjennom alle skript og funksjoner til den finner den heldige vinneren. Dette kan påvirke spillet ditt hvis du bruker det ofte på flere objekter, og hvis objektet har mange komponenter og skript (som er komponenter forresten). SendMessage søker bare objektet som samtalen er gjort, Sender BroadcastMessage et søk på målobjektet, men også på alle sine barn. Forskning vil bli enda lenger hvis objektet inneholder et stort hierarki. En nykommer dukket Opp Med Unity4 SendMessageUpwards som du burde ha gjettet, gjør det samme som BroadcastMessage, men oppover, så alle foreldrene til objektet til roten.

Ok la oss vurdere Vår StormTrooper har en funksjon for å motta ordre

//Order.csusing UnityEngine;using System.Collections;public class Order : MonoBehaviour{ private void ReceiveOrder(string order) { switch(order) { case "Fetch": //Action Debug.Log("Can you repeat Sir?"); break; // other cases } }}

nå la oss vurdere Vår StormTrooper møter en sjef Fra Imperiet:

// Commander.csprivate void Update(){ if(Input.GetKeyDown(KeyCode.Space)) { GameObject.Find("StormTrooper").SendMessage("ReceiveOrder","Fetch"); }}

som du kan se, forblir prinsippet det samme, jeg trenger en referanse til målobjektet og så sender jeg en melding til den. Akkurat Som GetComponent er det bedre hvis du har bufret objektreferansen.

det er også mulig å legge til en parameter for å sikre at meldingen finner en mottaker eller ikke.

Merk at det ikke er mulig å bruke en funksjon som returnerer en verdi. Du må da gå tilbake og bruke GetComponent.

Interaktive funksjoner

Unity bruker mange funksjoner som gjør at to eller flere objekter samhandler. Fordelen med disse funksjonene er at de lagrer mye informasjon om det andre objektet.

  1. Kollisjonsfunksjoner OnTriggerXXXX/Oncollisionxx
  2. Fysikkfunksjoner XXXXcast

Der går vi med kollisjonserklæring:

private void OnCollisionEnter(Collision colReference){}private void OnTriggerEnter(Collider colReference){}

anropet til funksjonen skaper en variabel Av Typen Kollisjon eller Collider. Funksjonen vil fylle denne forekomsten med informasjon om kollisjonen og vil holde en referanse til det andre objektet vi samhandler med.

prviate void OnCollisionEnter(Collision colReference){ GameObject goRef = colReference.gameObject; if(goRef.CompareTag("StormTrooper")) { Health script = goRef.GetComponent<Health>(); script.health -= 5; }}

Legg Merke til at jeg ikke trenger å lete etter det andre objektet som colReference Er Av Typen Kollisjon og inneholder en referanse til det objektet som medlem. Nå kan jeg utføre Min GetComponent som vanlig og få tilgang til min folkehelsevariabel.

Funksjoner fra Fysikkklassen har et lignende prinsipp. Dataene lagres ikke I En Kollisjon / Collider-forekomst, men i En RaycastHit-forekomst:

using UnityEngine;using System.Collections;public class CheckFlat : MonoBehaviour { private void Update() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { float result = Vector3.Dot(hit.normal,Vector3.up); if(Mathf.Abs(result) > 0.9) { Debug.Log("Ground is flat enough"); } else { Debug.Log("Not Flat Enough"); } } }}

jeg erklærer En variabel Av Typen RaycastHit, jeg sender den til funksjonen. RaycastHit er en struktur og deretter en verditype, jeg trenger ut søkeordet for å lage en referanse til den strukturen slik at funksjonen kan endre strukturen innenfor. Hvis du lurer på den prosessen, sjekk Ut Minnehåndteringsartikkelen og delen om verdi / referansetype.
Hva jeg gjør her er å sjekke hvor flat er overflaten jeg plasserer markøren på toppen av . Dette er nyttig for strategiske spill hvor du vil plassere en bygning, men ikke på en klippe.
jeg bruker prikkproduktet av terrenget normalt med Vektoren3.Opp og hvis resultatet er nær nok til 1 betyr at de er justert, bakken er flat nok for bygging.Selvfølgelig er Det Nødvendig med noen flere beregninger, da dette bare er for ett punkt, og du må sjekke det normale rundt, men du får ideen. Jeg opprettet samspill mellom museposisjonen og terrenget ved hjelp av en raycast.

en raycast inneholder et utgangspunkt og en retning. I vårt tilfelle er utgangspunktet konverteringen av museposisjonen til verdensposisjon og deretter en retning basert på kamerarotasjon og projeksjonsmatrise. Denne linjen går uendelig i den definerte retningen (du kan også angi en maksimal avstand), og hvis den treffer en collider, lagres informasjonen til den collider og kollisjonen i RaycastHit-strukturen. Strukturen inneholder da en referanse til treffobjektet.