Articles

GetComponent in C#

tässä opetusohjelmassa

  • toisen skriptin käyttäminen objektin sisältä
  • miten getcomponent toimii?
  • saa monia komponentteja
  • vuorovaikutus olioiden välillä.
  • hierarkiassa olevien objektien välinen vuorovaikutus
  • Sendmessage ja BroadcastMessage
  • Interaktiiviset funktiot

Johdanto

yksi toistuva kysymys ykseydestä aloitettaessa on se, miten yhden komentosarjan jäsenet pääsevät toisesta komentosarjasta. Moni luulisi, että käsikirjoitusnimen käyttäminen riittäisi, he tajuavat nopeasti, ettei se ole.

ohjelmaa kehitettäessä muuttujia tallennetaan muistiin eri paikkoihin. Jos objekti voisi nähdä muita objektin jäseniä, olisi riski muokata niitä, vaikka sitä ei olisi tarkoitettu. Jos tapauksissa kaksi objektia on sama skripti ja samat muuttujan nimet, kääntäjä ei voi selvittää, kumpaan viittaamme.

tämän ongelman estämiseksi jokainen muistissa oleva objekti ei voi nähdä muita objekteja. Tällöin on kerrottava yhdelle kohteelle, missä sen tarvitsema muuttuja on muistissa. Koko osoitinperiaate on pudotettu kauan sitten ja nyt käytämme viittausta sen sijaan (joka todella käyttää osoitinta sisäisesti).

muuttujiin pääsee käsiksi eri tavoin, osa on parempia kuin toiset ja osa on yksinkertaisesti tarkoitettu käytettäväksi erityistilanteissa.

  1. GetComponent, yleisin, myös aluksi eniten hämmentävä
  2. SendMessage, se saattaa näyttää helpommalta ymmärtää, mutta se on myös tehottomampi.
  3. staattiset muuttujat, helpoimmat mutta myös monimutkaisimmat ymmärtää aluksi täysin.

kohtauksen perustaminen

Luo uusi kohtaus, lisää tyhjä peliobjekti ja anna sille nimi ”StormTrooper”. Luo kaksi komentosarjaa, joiden nimet ovat ”Health”ja ” Force”. Lisää molemmat skriptit Stormtrooperiin.

avaa molemmat skriptit editorissa ja lisää nämä rivit:

Terveys.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; } } }}

voima.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; }}

tämän koodin yrittäminen palauttaa yksinkertaisesti virheen”objektiviittaus vaaditaan ei-staattisiin jäseniin pääsemiseksi”.

meidän on kerrottava Stormtrooperin Terveyskomponentille, missä on Voimakomponentti, jotta se pääsee jäseniinsä käsiksi. Koska skriptit ovat molemmat samassa objektissa, meidän on yksinkertaisesti julistettava muuttuja skriptin tyypin haluamme käyttää ja käyttää GetComponent löytää skripti.

toisen komentosarjan käyttäminen objektin sisällä

muokkaa terveyttä:

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; } } }}

muutoksilla, Suorita peli, paina välilyöntiä ja paina sitten P, Stormtrooperilla on nyt 10 terveyttä (huomaa, että Stormtrooperilla ei ole voimaa, tämä on vain esimerkki). Käytät muuttujia samalla tavalla kuin metodeja. Varmista vain, että jäsenet yrität tavoittaa julistetaan julkiseksi tai saat virheen.

Jos jäsenet olisivat yksityisiä, heihin ei olisi mahdollista päästä käsiksi. Kaiken, mitä ei pitäisi saavuttaa luokan/komentosarjan ulkopuolelta, pitäisi olla yksityistä kapselointiperiaatteen noudattamiseksi.

miten GetComponent vaikuttaa?

ensin julistamme objektin, jonka kirjoitustyypin haluamme saavuttaa.

private Force forceScript = null;

Force on tyyppimme ja forceScript on vain nimi, jonka annamme käyttää viittausta skriptissä. Voimme antaa sille minkä nimen tahansa.

GetComponent-funktio etsii kohteen (Stormtrooper-peliobjektin) sisältä ohittamaamme tyyppiä vastaavan komponentin. Jos mitään ei löydy, palautetaan null-viite ja forceScript-muuttuja ei tee mitään. Jos tyyppivoimakomponentti löytyy, niin kyseisen komponentin osoite siirretään muuttujan voimakomponentille, muuttuja osoittaa nyt jonnekin muistiin tallennetun Voimakomponentin. Jotta pääsemme käsiksi julkisiin voimiin, – meidän täytyy vain siirtää komentosarjamuuttuja pisteoperaattorin avulla. Sanotaan, että forceScript-muuttuja on kahva kohteeseen. Se antaa pääsyn kaikille julkisille jäsenille (ja sisäisille).

script.PublicMembers;

yksityiset ja suojellut jäsenet jäävät saavuttamattomiin.

käyttämällä serializefield-attribuuttia voidaan nähdä inspector desputen muuttujat yksityisinä. ForceScript-muuttuja alkaa arvolla ei mitään (voima) ja muuttuu Voimaviitteeksi. Jos klikkaat sitä, se korostaa kohdeobjektin.

reference_component

So I can cache component with GetComponent?

Kyllä, ja se on suositeltavaa tehdä joillekin säännöllisesti käytetyille aineosille.

käytettäessä sisäänrakennettuja komponentteja, kuten Rigidbody, Transform, Renderer,…jne, Unity käytti mahdollistaakseen ”suoran” pääsyn näin:

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

he poistivat ne kaikki paitsi muunnoksen, koska aina on muunnos missä tahansa peliobjektissa.

GetComponent tulee välttää päivityksen sisällä, joten välimuistiin tallentamista suositellaan muutamalla yksinkertaisella koodirivillä, kuten:

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);}

Tämä säästää jonkin verran laskentaa.

saa monia komponentteja

Mitä jos Stormtrooperilla on paljon voimaa itsessään (ei kuitenkaan todennäköistä). Kopioitu voima kahdesti, joten Stormtrooperilla on kolme.

terveys tulee:

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; } } } }}

Run the game, press Space, our StormTrooper is becoming a Jedi. GetComponents etsii kaikki osat tyyppi ja palauttaa joukon niistä. Tämä on hieno, jos et kohdistu tiettyyn osaan tyyppiä, mutta todennäköisesti kaikki ne.

katso seuraavat valot päälle ja pois päältä:

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; } } }}

esineiden välinen vuorovaikutus.

voimme myös saada kaksi erilaista objektia vuorovaikutukseen samalla tavalla. Luo uusi objekti nimeltä ”Sith”. Poista päällekkäiset komponentit Stormtrooperista niin vain yksi on jäljellä.

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."); } }}

muokkaa Stormtrooperin voimakäsikirjoitusta niin, että hänellä ei ole voimaa (sithit pitäisivät tätä epäilyttävänä…), annan vihjeen, että se tapahtuu alku-skriptissä.

alla on viiva, joka löytää Stormtrooperin ja saa sen Voimakomponentin.

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

GameObject.Etsi () ensin löytää objektin läpäissyt parametrina ja sitten etsii tarvittavan komponentin sen sisällä. On suositeltavaa suorittaa nämä toimet alussa tai aikaan pelin, jossa viiveet eivät vaikuta pelattavuus. GameObject.Etsi ja GetComponent ovat kalliita toimintoja, koska ne täytyy iteroida läpi koko hierarkia. Jos kohtauksesi sisältää vain 10 esinettä, se on hieno, mutta jos siinä on tuhansia niitä (klooniarmeija), näet varmasti huonon vaikutuksen. Harkitse kutsumalla näitä Start-funktiossa. Huomaa, että jos kohtauksessasi on useita Stormtroopereita, Uity ei voi tietää, mitä aiot käyttää, ja palauttaa ensimmäisen löytämänsä. Todennäköisesti sinulla on viittaus kohteeseen, jonka kanssa haluat olla vuorovaikutuksessa, esimerkiksi törmäyskutsu.

hierarkiassa olevien kohteiden välinen vuorovaikutus

pitävät ajoneuvoa, sillä ajettaessa merkki on kiinnitetty siihen lapsiobjektina. Autossa olisi erityisominaisuus, jos kuljettajalla on voimaa (parempi käsittely, nopeampi ja niin edelleen). Kun kuljettaja on kiinni, hän on hierarkiassa alapuolella.
ajoneuvo löytää kuljettajan Voimakomponentin samalla periaatteella kuin aiemmin (GameObject.Etsi), mutta voimme saada sen tapahtumaan nopeammin osoittamalla ajoneuvolle, että kuljettaja on omassa hierarkiassaan.

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

GameObject vaihdetaan muunnokseen. Itse asiassa, lapsi esine kuuluu muutos vanhemman joten meidän täytyy tutkia muutos löytää objekti. Esimerkissä kohde on aivan kutsuvan objektin alla, jos Sith on ohjaamon objektin sisällä auton objektin alla, sinun täytyy antaa polku:

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

kutsuobjekti on auto ja sitä kutsutaan kutsussa tällä nimellä. Sitten tutkitaan ohjaamo ja Sith. Jos polku on väärä, Etsi palauttaa null ja saat Null viite poikkeus. Sen voi välttää näin:

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

muunnos.Find palauttaa muunnoksen, kun GameObject.Find palauttaa GameObject-tiedoston.

vielä paremmin:

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

nyt ajoneuvomme voi tietää, että meidän Sithissä on voimaa ja soveltaa erikoisominaisuutta.
Yksi tiedettävä asia, jos pääobjekti sisältää etsimäsi tyyppisen skriptin, se palauttaa sen. Kun siis ajattelet olevasi tekemisissä lapsen osan kanssa, niin olet todellisuudessa tekemisissä vanhemman kanssa.

tässä esimerkissä on tarkistettava, onko kyseessä StormTrooper vai Sith. Parempi lähestymistapa olisi ollut käyttää periytymistä niin, että ylimmässä perusluokassa olisi voimamuuttuja ja yksi ainoa funktio Olisi käytössä kaikille kuljettajatyypeille. Mutta perintö ja tehokkuus eivät olleet tässä pääasia.

SendMessage ja BroadcastMessage

SendMessage ja BroadcastMeassage ovat kaksi muuta mahdollisuutta, jotka voivat olla yksinkertaisempia mutta myös kalliimpia. Periaate pysyy samana, meillä on viittaus objektiin ja haluamme muokata sen sisältämää skriptiä.

Sendmessagen avulla funktiota voi kutsua objektiin ilman, että tarvitsee löytää viittausta skriptiin. Nyt kerrot minulle: ”mitä tämä hämmennys on, jos pystyt tekemään sen yksinkertaiseksi?”

SendMessage käyttää heijastusta ja on erittäin hidas verrattuna Getcomponentiin, ja sitä tulee harkita vain tapauksissa, joissa se ei vaikuta peliin tai jos muuta ratkaisua ei ole.

ja sen pitäisi kuulostaa loogiselta, Getcomponentilla osoitamme tarkalleen missä kirjoitus sijaitsee muistissa. Sendmessagen avulla kääntäjä tuntee kohteen, mutta se käy läpi kaikki skriptit ja toiminnot, kunnes löytää onnekkaan voittajan. Tämä voi vaikuttaa peliisi, jos käytät sitä usein useissa kohteissa ja jos objektissa on paljon komponentteja ja skriptejä (jotka ovat muuten komponentteja).

Sendmessage etsii vain kohteen, johon puhelu soitetaan, BroadcastMessage käynnistää haun kohteesta, mutta myös kaikista sen lapsista. Tutkimus kestää vielä pidempään, jos kohde sisältää suuren hierarkian. Tulokas ilmestyi Unity4 SendMessageUpwards joka sinun olisi pitänyt arvata ei sama kuin BroadcastMessage mutta ylöspäin niin kaikki vanhemmat objektin juureen asti.

OK katsotaan, että Stormtrooperilla on funktio käskyn vastaanottamiseen

//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 } }}

nyt tarkastellaan Stormtrooperiamme kohtaa imperiumin komentajan:

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

kuten näette, periaate pysyy samana, tarvitsen viittauksen kohdekohtaan ja sitten lähetän sille viestin. Aivan kuten GetComponent se on parempi, jos olet välimuistissa objekti viittaus.

on myös mahdollista lisätä parametri, jolla varmistetaan, että viesti löytää vastaanottajan vai ei.

huomaa, että arvoa palauttavaa funktiota ei ole mahdollista käyttää. Sitten sinun pitäisi mennä takaisin ylös ja käyttää GetComponent.

Interaktiiviset funktiot

Unity käyttää monia ominaisuuksia, jotka saavat kaksi tai useampia objekteja vuorovaikutukseen. Näiden toimintojen etuna on, että ne tallentavat paljon tietoa toisesta kohteesta.

  1. Törmäysfunktiot OnTriggerXXXX/OnCollisionXXXX
  2. Fysiikkafunktiot XXXXcast

siellä mennään törmäysilmoituksella:

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

funktion kutsusta syntyy tyypiltään Törmäysmuuttuja tai törmääjä. Funktio täyttää tämän instanssin tiedolla törmäyksestä ja sisältää viittauksen toiseen objektiin, jonka kanssa olemme vuorovaikutuksessa.

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

huomaa, että minun ei tarvitse etsiä toista kohdetta, sillä colReference on tyypiltään Collision ja sisältää viittauksen kyseiseen esineeseen jäsenenä. Nyt voin suorittaa Getcomponentin tavalliseen tapaan ja käyttää kansanterveysmuuttujaani.

fysiikan luokan funktioilla on samanlainen periaate. Tietoja ei tallenneta törmäys – / törmäys-instanssiin vaan RaycastHit-instanssiin:

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"); } } }}

julistan tyypin Raycasthitin muuttujan, siirrän sen funktiolle. RaycastHit on rakenne ja sitten arvo tyyppi, tarvitsen ulos avainsanan luoda viittaus, että rakenne, jotta funktio voi muokata rakennetta sisällä. Jos mietit tätä prosessia, tutustu muistinhallinnan artikkeli ja osa arvo / viite tyyppi.
mitä teen tässä on tarkistaa, kuinka tasainen on pinta, jonka päälle asetan kursorin. Tämä on hyödyllinen strategisia pelejä, joissa haluat sijoittaa rakennuksen, mutta ei kalliolla.
käytän vector3: n kanssa maaston normaalin pistetuloa.Ylös ja jos tulos on tarpeeksi lähellä 1 eli ne ovat linjassa, maa on riittävän tasainen rakentamiseen.
ilmeisesti tarvitaan lisää laskutoimituksia, koska tämä on vain yhden pisteen osalta ja normaalit pitäisi tarkistaa ympäriltä, mutta saat idean. Loin hiiriasennon ja maaston välisen vuorovaikutuksen raycastin avulla.

raycast sisältää lähtöpisteen ja suunnan. Meidän tapauksessamme lähtökohtana on hiiren aseman muuntaminen maailman asentoon ja sen jälkeen kameran pyörimis-ja projektiomatriisiin perustuva suunta. Tämä linja kulkee äärettömästi määriteltyyn suuntaan (voit myös asettaa maksimietäisyyden) ja jos se osuu törmäyttimeen, kyseisen törmäyttimen ja törmäyksen tiedot tallennetaan Raycasthitin rakenteeseen. Rakenne sisältää sitten viittauksen osuneeseen kappaleeseen.