Articles

GetComponent în C #

în acest tutorial

  • accesarea unui alt script din interiorul obiectului
  • cum funcționează GetComponent?
  • Noțiuni de bază mai multe componente
  • interacțiunea dintre obiecte.
  • interacțiunea dintre obiectele din ierarhia
  • SendMessage și BroadcastMessage
  • funcții Interactive

Introducere

o problemă recurentă atunci când începe cu unitatea este Cum de a accesa membrii într-un script de la un alt script. Mulți ar crede că dereferențierea de la numele scenariului ar fi suficientă, își dau seama repede că nu este.

la dezvoltarea unui program, variabilele sunt stocate în memorie în diferite locații. Dacă un obiect ar putea vedea alți membri ai obiectului, ar exista riscul de a-i modifica, chiar dacă nu a fost intenționat. Dacă pentru instanțe două obiecte dețin același script cu aceleași nume de variabile, compilatorul nu ar putea să-și dea seama la care ne referim.

pentru a preveni această problemă, fiecare obiect din memorie nu poate vedea alte obiecte. Apoi este necesar să spuneți unui obiect unde variabila de care are nevoie este în memorie. Întregul principiu pointer a fost abandonat cu mult timp în urmă și acum folosim în schimb referință (care folosește de fapt pointer intern).

există diferite moduri de a accesa variabile, unele sunt mai bune decât altele, iar unele sunt pur și simplu pentru a fi utilizate în situații particulare.

  1. GetComponent, cel mai frecvent, de asemenea, cel care confundă cel mai mult la început
  2. SendMessage, s-ar putea arata mai ușor de înțeles, dar este, de asemenea, mai puțin eficient.
  3. variabile statice, cea mai ușoară, dar și cea mai complicată pentru a înțelege pe deplin la început.

configurarea unei scene

creați o scenă nouă, adăugați obiecte de joc goale și denumiți-o „StormTrooper”. Creați două scripturi numite” Sănătate „și”forță”. Adăugați ambele Scripturi la StormTrooper.

deschideți ambele scripturi în editor și adăugați aceste linii:

sănătate.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; } } }}

forță.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; }}

încercarea acestui cod va returna pur și simplu eroarea „este necesară o referință obiect pentru a accesa membrii non-statici”.

trebuie să spunem componentei de sănătate de pe StormTrooper unde este componenta forței, astfel încât să poată accesa membrii săi. Deoarece scripturile noastre sunt ambele pe același obiect, trebuie doar să declarăm o variabilă de tip script pe care dorim să o accesăm și să o folosim GetComponent pentru a găsi scriptul.

accesarea unui alt script în interiorul obiectului

Modify sănătate cu:

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

cu modificările, rula jocul, apăsați spațiu și apoi apăsați P, StormTrooper dvs. are acum 10 de sănătate (rețineți că un StormTrooper nu are forța, aceasta este doar pentru exemplu). Accesați variabilele în același mod în care ați accesat metodele. Asigurați-vă că membrii la care încercați să ajungeți sunt declarați publici sau veți primi o eroare.

dacă membrii ar fi privați, nu ar fi posibil să le accesați. Orice lucru care nu ar trebui să fie atins din afara clasei/scriptului ar trebui să fie privat pentru a face față principiului încapsulării.

cum funcționează GetComponent?

Mai întâi declarăm un obiect de tipul scriptului la care dorim să ajungem.

private Force forceScript = null;

forța este tipul nostru și forceScript este doar un nume pe care îl oferim pentru utilizarea referinței în script. I-am putea da orice nume.

funcția GetComponent va privi în interiorul obiectului (obiectul jocului StormTrooper) pentru a găsi o componentă corespunzătoare tipului pe care l-am trecut. Dacă nu se găsește nici unul, se returnează o referință nulă și variabila noastră forceScript nu va face nimic. Dacă se găsește o componentă de tip Force, atunci adresa acelei componente este transmisă variabilei forceScript, variabila indică acum componenta Force stocată undeva în memorie. Pentru a accesa membrii publici de forță, trebuie doar să dereference variabila script folosind operatorul dot. Se spune că variabila forceScript este un mâner pentru obiect. Oferă acces tuturor membrilor publici (și celor interni).

script.PublicMembers;

membrii privați și protejați vor rămâne inaccesibili.

utilizarea atributului SerializeField permite vizualizarea variabilelor din inspector despute fiind private. Variabila forceScript începe cu valoarea None (Force) și se transformă într-o referință de forță. Dacă faceți clic pe el, acesta va evidenția obiectul țintă.

reference_component

deci, pot cache componenta cu GetComponent?

Da, și este recomandat să faceți acest lucru pentru unele componente utilizate în mod regulat.

la accesarea componentelor încorporate, cum ar fi Rigidbody, Transform, Renderer,…etc, Unity a folosit pentru a permite accesul „direct” astfel:

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

le-au eliminat pe toate, cu excepția transformării, deoarece există întotdeauna o transformare pe orice obiect de joc dat.

GetComponent ar trebui să fie evitate în interiorul actualizare astfel cache este recomandat cu câteva linii simple de cod cum ar fi:

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

aceasta va salva unele calcule.

Noțiuni de bază mai multe componente

Ce se întâmplă dacă StormTrooper nostru are multe vigoare în sine (nu probabil, deși). Duplicați forța de două ori, astfel încât StormTrooper să aibă trei dintre ele.

sănătatea devine:

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

rulați jocul, apăsați Space, StormTrooper nostru devine un Jedi. GetComponents caută toate componentele de tip și returnează o serie de ele. Acest lucru este bine dacă nu vizați o componentă specifică a tipului, dar mai probabil toate acestea.

vedeți următoarele ar aprinde și stinge luminile:

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

interacțiunea dintre obiecte.

putem obține, de asemenea, două obiecte diferite pentru a interacționa într-un mod similar. Creați un obiect nou numit”Sith”. Scoateți componentele duplicate din StormTrooper, astfel încât să rămână doar unul.

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

modificați scriptul de forță al Stormtrooperului, astfel încât să nu aibă forța (Sith ar găsi acest suspect…), vă dau un indiciu că se întâmplă în scriptul de pornire.

mai jos este linia care găsește StormTrooper și obține componenta sa de forță.

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

GameObject.Find () găsește mai întâi obiectul trecut ca parametru și apoi caută componenta necesară în interiorul acestuia. Se recomandă efectuarea acelor acțiuni la început sau într-un moment al jocului în care întârzierile nu vor afecta jocul. GameObject.Găsiți și GetComponent sunt funcții scumpe, deoarece au nevoie pentru a itera prin întreaga ierarhie. Dacă scena dvs. include doar 10 obiecte, va fi bine, dar dacă deține mii dintre ele (o armată de clone), veți vedea cu siguranță un efect rău. Luați în considerare apelarea acestora în funcția Start. Rețineți că, dacă scena dvs. conține mulți StormTroopers, Uity nu poate ști ce ați vrut să utilizați și îl va returna pe primul pe care îl găsește. Cel mai probabil, dețineți o referință la obiectul cu care doriți să interacționați, de exemplu un apel invers de coliziune.

interacțiunea dintre obiectele din ierarhia

luați în considerare un vehicul,, atunci când îl conduceți, caracterul este atașat la acesta ca un obiect copil. Vehiculul ar avea o caracteristică specială dacă șoferul are forța (o manevrabilitate mai bună, mai rapidă și așa mai departe). Pe măsură ce șoferul este atașat, el este mai jos în ierarhie.
vehiculul poate găsi componenta de forță a conducătorului auto cu același principiu am folosit anterior (GameObject.Găsi) dar putem face acest lucru mai rapid indicând vehiculului că șoferul se află în propria ierarhie.

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

GameObject este schimbat pentru transformare. Într-adevăr, un obiect copil aparține transformării părintelui, așa că trebuie să ne uităm în transformare pentru a ne găsi obiectul. În exemplu, obiectul este chiar sub obiectul apelant, dacă Sith-ul dvs. se află în interiorul obiectului cockpit sub obiectul mașinii, trebuie să furnizați calea:

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

obiectul apelant este mașina și este menționat ca acest lucru în apel. Apoi Cockpit este căutat și Sith este căutat. În cazul în care calea este greșită, găsi returnează null și veți obține o excepție de referință Null. Puteți evita asta astfel:

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

transformare.Găsiți returnează o transformare în timp ce GameObject.Găsiți returnează un GameObject.

și mai bine, putem folosi:

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

acum vehiculul nostru poate ști că Sith-ul nostru are forța și aplică o caracteristică specială.
O problemă de știut, dacă obiectul părinte conține un script de tipul pe care îl căutați, îl va returna. Deci, gândindu – vă că aveți de-a face cu componenta copil, de fapt aveți de-a face cu cea părinte.

în acest exemplu, trebuie să verificăm pentru fiecare tip de șofer dacă este un StormTrooper sau un Sith. O abordare mai bună ar fi fost utilizarea moștenirii, astfel încât clasa de bază superioară să dețină variabila de forță și o singură funcție să fie utilizată pentru toate tipurile de șofer. Dar moștenirea și eficiența nu au fost principala noastră problemă aici.

SendMessage și BroadcastMessage

SendMessage și Broadcastmeasage sunt alte două posibilități care pot fi mai simple, dar și mai scumpe. Principiul rămâne același, avem o referință la un obiect și dorim să modificăm un script pe care îl conține.

cu SendMessage, puteți apela o funcție pe un obiect fără a fi nevoie să găsiți o referință la script. Acum spune-mi: „ce este toată această plimbare confuzie dacă puteți face simplu?”

SendMessage folosește reflecția și este extrem de lent în comparație cu GetComponent și ar trebui să fie luate în considerare numai în cazurile în care nu va afecta jocul sau dacă nu aveți altă soluție.

și asta ar trebui să sune logic, cu GetComponent indicăm exact unde se află scriptul în memorie. Cu SendMessage, compilatorul cunoaște obiectul, dar va trece prin toate scripturile și funcțiile până când va găsi norocosul câștigător. Acest lucru vă poate afecta jocul dacă îl utilizați des pe mai multe obiecte și dacă obiectul are o mulțime de componente și scripturi (care sunt componente apropo).

SendMessage caută doar obiectul la care se face apelul, BroadcastMessage lansează o căutare pe obiectul țintă, dar și pe toți copiii săi. Cercetarea va fi și mai lungă dacă obiectul conține o ierarhie mare. Un nou venit a apărut cu Unity4 SendMessageUpwards pe care ar fi trebuit să-l ghiciți face același lucru ca BroadcastMessage, dar în sus, astfel încât toți părinții obiectului până la rădăcină.

ok să considerăm StormTrooper nostru are o funcție pentru a primi comanda

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

acum să considerăm StormTrooper nostru întâlnește un comandant din Imperiu:

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

după cum puteți vedea, principiul rămâne același, am nevoie de o referință la obiectul țintă și apoi am trimite un mesaj la ea. La fel ca GetComponent este mai bine dacă ați memorat în cache Referința obiectului.

de asemenea, este posibil să adăugați un parametru pentru a vă asigura că mesajul găsește un receptor sau nu.

rețineți că nu este posibilă utilizarea unei funcții care returnează o valoare. Apoi va trebui să vă întoarceți și să utilizați GetComponent.

funcții Interactive

Unity folosește multe caracteristici care fac ca două sau mai multe obiecte să interacționeze. Avantajul acestor funcții este că stochează o mulțime de informații despre celălalt obiect.

  1. funcții de coliziune OnTriggerXXXX/OnCollisionXXXX
  2. funcții de Fizică XXXXcast

acolo mergem cu declarația de coliziune:

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

apelul funcției creează o variabilă de tip coliziune sau coliziune. Funcția va completa această instanță cu informații despre coliziune și va conține o referință la celălalt obiect cu care interacționăm.

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

observați că nu trebuie să caut celălalt obiect, deoarece colreferența este de tip coliziune și conține o referință la acel obiect ca membru. Acum îmi pot efectua GetComponent ca de obicei și pot accesa variabila mea de sănătate publică.

funcțiile din clasa fizică au un principiu similar. Datele nu sunt stocate într-o instanță de coliziune/coliziune, ci într-o instanță RaycastHit:

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

declar o variabilă de tip RaycastHit, o transmit funcției. RaycastHit este o structură și apoi un tip de valoare, am nevoie de cuvântul cheie out pentru a crea o referință la acea structură, astfel încât funcția să poată modifica structura din interior. Dacă vă întrebați despre acest proces, consultați articolul de gestionare a memoriei și secțiunea despre tipul de valoare/referință.
ceea ce fac aici este să verific cât de plană este suprafața pe care așez cursorul deasupra. Acest lucru este util pentru jocurile strategice în care doriți să plasați o clădire, dar nu pe o stâncă.
eu folosesc produsul punct al terenului normal cu Vectorul3.În sus și dacă rezultatul este suficient de aproape de 1, ceea ce înseamnă că sunt aliniate, solul este suficient de plat pentru construcție.
evident, sunt necesare mai multe calcule, deoarece acest lucru este doar pentru un punct și va trebui să verificați normalul în jur, dar veți obține ideea. Am creat interacțiunea dintre poziția mouse-ului meu și teren folosind un raycast.

un raycast conține un punct de plecare și o direcție. În cazul nostru, punctul de plecare este conversia poziției mouse-ului în poziția mondială și apoi o direcție bazată pe rotația camerei și matricea de proiecție. Această linie merge infinit în direcția definită (puteți seta, de asemenea, o distanță maximă) și dacă lovește un colizor, informațiile despre acel colizor și coliziune sunt stocate în structura RaycastHit. Structura conține apoi o referință la obiectul lovit.