Articles

GetComponent v C#

V tomto tutoriálu

  • Přístup k dalším skript uvnitř objektu
  • Jak se GetComponent práce?
  • získání mnoha komponent
  • interakce mezi objekty.
  • Interakce mezi objekty v rámci hierarchie
  • SendMessage a BroadcastMessage
  • Interaktivní funkce

Úvod

Jeden opakující se problém, když se začíná s Unity, je, jak se přístup členů v jednom skriptu z jiného skriptu. Mnozí by si mysleli, že by stačilo dereferencování z názvu scénáře, rychle si uvědomí, že tomu tak není.

při vývoji programu jsou proměnné uloženy v paměti na různých místech. Pokud by objekt mohl vidět jiné členy objektu, hrozilo by riziko jejich úpravy, i když to nebylo zamýšleno. Pokud pro instance dva objekty obsahují stejný skript se stejnými názvy proměnných, kompilátor by nebyl schopen zjistit, na který z nich odkazujeme.

aby se zabránilo tomuto problému, každý objekt v paměti nemůže vidět jiné objekty. Pak je nutné říct jednomu objektu, kde je proměnná, kterou potřebuje, v paměti. Celý princip ukazatele byl již dávno zrušen a nyní místo toho používáme odkaz (který ve skutečnosti používá ukazatel interně).

existují různé způsoby přístupu k proměnným, některé jsou lepší než jiné a některé jsou jednoduše použity v konkrétních situacích.

  1. GetComponent, nejběžnější, také ten, který zpočátku nejvíce zaměňuje
  2. SendMessage, může vypadat snadněji uchopitelný, ale je také méně efektivní.
  3. statické proměnné, nejjednodušší, ale také nejsložitější na první pohled.

nastavení scény

vytvoření nové scény, přidání prázdných herních objektů a pojmenování „StormTrooper“. Vytvořte dva skripty s názvem „zdraví“ a „síla“. Přidejte oba skripty do StormTrooper.

otevřete oba skripty v editoru a přidejte tyto řádky:

zdraví.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; } } }}

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

Pokus o tento kód jednoduše vrátí chybu „pro přístup k nestatickým členům je vyžadován odkaz na objekt“.

musíme říct zdravotní komponentě na Stormtrooperu, kde je složka síly, aby mohla přistupovat ke svým členům. Protože naše skripty jsou oba na stejný objekt, prostě musíme deklarovat proměnnou typu skriptu přejeme, aby přístup a používat GetComponent najít ten skript.

Přístup k další skript uvnitř objektu

Změnit Zdravotnictví s:

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

S úpravami, spustit hru, stiskněte mezerník a poté stiskněte tlačítko P, své StormTrooper má nyní 10 zdravotnictví (Všimněte si, že StormTrooper nemá sílu, to je jen pro příklad). K proměnným přistupujete stejným způsobem jako k metodám. Jen se ujistěte, že členové, které se snažíte oslovit, jsou prohlášeni za veřejné, nebo dojde k chybě.

Pokud by členové byli soukromí, nebylo by k nim možné přistupovat. Cokoli, co by nemělo být dosaženo mimo třídu / skript, by mělo být soukromé, aby se vyrovnalo se zásadou zapouzdření.

jak přípravek GetComponent funguje?

nejprve deklarujeme objekt typu skriptu, kterého chceme dosáhnout.

private Force forceScript = null;

Force je náš typ a forceScript je pouze název, který poskytujeme pro použití odkazu ve skriptu. Můžeme mu dát jakékoliv jméno.

GetComponent funkce bude vypadat uvnitř objektu (StormTrooper herní objekt) najít složku odpovídající typu, který jsme minuli. Pokud není nalezen žádný, vrátí se nulový odkaz a naše proměnná forceScript nic neudělá. Pokud je nalezena komponenta typu Force, pak je Adresa této komponenty předána proměnné forceScript, proměnná nyní ukazuje na komponentu Force uloženou někde v paměti. Pro přístup k veřejným členům Force stačí dereference proměnné skriptu pomocí operátoru teček. Říká se, že proměnná forceScript je popisovač objektu. Poskytuje přístup všem veřejným členům (a interním).

script.PublicMembers;

soukromí a chránění členové zůstanou nepřístupní.

použití atributu SerializeField umožňuje vidět, že proměnné v inspector despute jsou soukromé. Vaše proměnná forceScript začíná hodnotou None (síla) a změní se na odkaz síly. Pokud na něj kliknete, zvýrazní cílový objekt.

reference_component

takže mohu cache komponentu s GetComponent?

Ano, a doporučuje se to u některých pravidelně používaných komponent.

Při přístupu k vestavěné komponenty, jako jsou Rigidbody, Transformovat, Renderer,…atd, Unity používá k tomu, aby „přímý“ přístup, jako je tento:

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

odstranit všechny je ale proměnit, protože tam je vždy Transformovat na daný herní objekt.

GetComponent je třeba se vyhnout uvnitř aktualizace, takže ukládání do mezipaměti se doporučuje s několika jednoduchými řádky kódu, jako je:

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 se ušetří některé výpočty.

získání mnoha komponent

Co když má náš StormTrooper v sobě mnoho síly (není pravděpodobné). Duplikujte sílu dvakrát, takže StormTrooper má tři z nich.

zdraví se stává:

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

spusťte hru, stiskněte prostor, náš StormTrooper se stává Jedi. GetComponents vyhledá všechny komponenty typu a vrátí řadu z nich. To je v pořádku, pokud necílíte na konkrétní součást typu, ale pravděpodobně na všechny.

viz následující rozsvícení a vypnutí světel:

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

interakce mezi objekty.

můžeme také získat dva různé objekty k interakci podobným způsobem. Vytvořte nový objekt s názvem „Sith“. Odstraňte duplikované komponenty ze Stormtrooperu, takže zbývá pouze jedna.

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

Změnit platnost scénář StormTrooper tak, že nemá sílu (Sith by to podezřelé…), a já vám dám nápovědu se to stane v Začátku skriptu.

níže je řádek, který najde StormTrooper a získá jeho složku síly.

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

GameObject.Find () nejprve najde objekt předaný jako parametr a poté vyhledá požadovanou součást uvnitř. Doporučuje se provádět tyto akce na začátku nebo v době hry, kde zpoždění neovlivní hru. GameObject.Najít a GetComponent jsou drahé funkce, protože potřebují iterovat celou hierarchií. Pokud vaše scéna obsahuje pouze objekty 10, bude to v pořádku ,ale pokud jich bude mít tisíce (armáda klonů), určitě uvidíte špatný efekt. Zvažte jejich volání ve funkci Start. Všimněte si, že pokud vaše scéna obsahuje mnoho stormtrooperů, Uity nemůže vědět, které jste chtěli použít, a vrátí první, kterou najde. S největší pravděpodobností držíte odkaz na objekt, se kterým chcete komunikovat, například zpětné volání kolize.

interakce mezi objekty v hierarchii

zvažte vozidlo, při jeho řízení je k němu znak připojen jako podřízený objekt. Vozidlo by mělo zvláštní funkci, pokud má řidič sílu (lepší ovladatelnost, rychlejší atd.). Jak je řidič připojen, je v hierarchii níže.
vozidlo může najít silovou složku řidiče se stejným principem, jaký jsme použili dříve (GameObject.Najít) ale můžeme to udělat rychleji tím, že označíme vozidlu, že řidič je ve své vlastní hierarchii.

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

GameObject je vyměněn za transformaci. Vskutku, podřízený objekt patří k transformaci rodiče, takže se musíme podívat do transformace, abychom našli náš objekt. V příkladu je objekt přímo pod volajícím objektem, pokud je váš Sith uvnitř objektu kokpitu pod objektem automobilu, musíte zadat cestu:

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

volající objekt je auto a je ve volání označován jako toto. Poté je prohledán kokpit a prohledán Sith. Pokud je cesta nesprávná, Najděte vrací null a dostanete výjimku Null Reference. Tomu se můžete vyhnout takto:

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

transformace.Najít vrátí transformaci, zatímco GameObject.Najít vrátí GameObject.

Ještě lépe, můžeme použít:

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

Nyní naše vozidla může vědět, že naše Sith má sílu a použít speciální funkce.
Jeden problém vědět, pokud nadřazený objekt obsahuje skript typu, který hledáte, bude to vrátit. Takže si myslíte, že máte co do činění s dětskou složkou, ve skutečnosti jednáte s rodičovskou.

v tomto příkladu musíme zkontrolovat pro každý druh ovladače, zda se jedná o StormTrooper nebo Sith. Lepším přístupem by bylo použít dědičnost tak, aby nejvyšší základní třída držela proměnnou síly a pro všechny typy ovladačů by byla použita jedna jediná funkce. Ale dědičnost a efektivita zde nebyly naším hlavním problémem.

SendMessage a BroadcastMessage

SendMessage a BroadcastMeassage jsou další dvě possibilties, že může být jednodušší, ale také dražší. Princip zůstává stejný, máme odkaz na objekt a chceme upravit skript, který obsahuje.

S SendMessage můžete volat funkci na objektu, aniž byste museli najít odkaz na skript. Teď mi řeknete: „co je to za zmatek, když to dokážete zjednodušit?“

SendMessage používá odraz a je extrémně pomalý ve srovnání s Getcomponentem a měl by být zvažován pouze v případech, kdy to neovlivní hru nebo pokud nemáte jiné řešení.

a to by mělo znít logicky, s GetComponent označíme přesně, kde je skript umístěn v paměti. S SendMessage kompilátor zná objekt, ale projde všemi skripty a funkcemi, dokud nenajde šťastného vítěze. To může ovlivnit vaši hru, pokud ji často používáte na více objektech a pokud má objekt mnoho komponent a skriptů(což jsou mimochodem komponenty).

SendMessage hledá pouze objekt, ke kterému je hovor uskutečněn, BroadcastMessage spustí vyhledávání na cílovém objektu, ale také na všech svých dětech. Výzkum bude ještě delší, pokud objekt obsahuje velkou hierarchii. Objevil se nováček s Unity4 SendMessageUpwards, který jste měli uhodnout, dělá to samé jako BroadcastMessage, ale nahoru, takže všichni rodiče objektu až do root.

Dobře podívejme se na naše StormTrooper má funkci pro příjem, aby

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

Nyní pojďme zvážit naše StormTrooper splňuje velitel od Empire:

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

Jak můžete vidět, princip zůstává stejný, potřebuji odkaz na cílový objekt a pak jsem se poslat zprávu na něj. Stejně jako GetComponent je lepší, pokud jste uložili do mezipaměti odkaz na objekt.

je také možné přidat parametr, aby se ujistil, že zpráva najde přijímač nebo ne.

Všimněte si, že není možné použít funkci vracející hodnotu. Pak byste se museli vrátit a použít GetComponent.

interaktivní funkce

Unity používá mnoho funkcí, které způsobují interakci dvou nebo více objektů. Výhodou těchto funkcí je, že ukládají spoustu informací o druhém objektu.

  1. Kolize funkce OnTriggerXXXX/OnCollisionXXXX
  2. Fyzika funkce XXXXcast

Tam jsme jít s kolize prohlášení:

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

volání funkce vytvoří proměnnou typu Kolize, nebo Urychlovač. Funkce vyplní tuto instanci informacemi o kolizi a bude obsahovat odkaz na jiný objekt, se kterým komunikujeme.

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

Všimněte si, že nepotřebuji hledat jiný objekt jako colReference je typu Kolize a obsahuje odkaz na objekt jako člen. Nyní mohu provádět svůj GetComponent jako obvykle a přistupovat k mé proměnné veřejného zdraví.

funkce z třídy fyziky mají podobný princip. Data nejsou uložena v instanci Collision/Collider, ale v instanci 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"); } } }}

deklaruji proměnnou typu RaycastHit, předávám ji funkci. RaycastHit je struktura a pak typ hodnoty, potřebuji Klíčové slovo out k vytvoření odkazu na tuto strukturu, aby funkce mohla upravit strukturu uvnitř. Pokud vás tento proces zajímá, podívejte se na článek správa paměti a část o typu hodnota / reference.
to, co zde dělám, je zkontrolovat, jak plochý je povrch, na který umístím kurzor. To je užitečné pro strategické hry, kde chcete umístit budovu, ale ne na útesu.
používám tečkový součin terénního normálu s Vektorem3.Nahoru a pokud je výsledek dostatečně blízko 1 což znamená, že jsou zarovnány, země je dostatečně plochá pro stavbu.
je zřejmé, že jsou zapotřebí další výpočty, protože je to pouze pro jeden bod a budete muset zkontrolovat normální okolí, ale dostanete nápad. Pomocí raycastu jsem vytvořil interakci mezi polohou myši a terénem.

raycast obsahuje výchozí bod a směr. V našem případě je výchozím bodem přeměna polohy myši na světovou pozici a poté směr založený na matici rotace a projekce kamery. Tato čára jde nekonečně v definovaném směru (můžete také nastavit maximální vzdálenost) a pokud zasáhne urychlovač, informace o tomto urychlovači a kolizi jsou uloženy ve struktuře RaycastHit. Struktura pak obsahuje odkaz na zasažený objekt.