Articles

GetComponent in C #

ebben az oktatóanyagban

  • egy másik szkript elérése az objektum belsejéből
  • hogyan működik a GetComponent?
  • sok összetevő beszerzése
  • interakció az objektumok között.
  • interakció a hierarchián belüli objektumok között
  • SendMessage és BroadcastMessage
  • interaktív függvények

Bevezetés

az Unity-vel való kezdéskor az egyik visszatérő probléma az, hogy hogyan lehet elérni a tagokat egy szkriptben egy másik szkriptből. Sokan azt gondolnák, hogy elegendő lenne a szkript nevéből való hivatkozás, gyorsan rájönnek, hogy nem az.

program fejlesztésekor a változók különböző helyeken tárolódnak a memóriában. Ha egy objektum más objektumtagokat látna, fennáll annak a veszélye, hogy módosítja őket, annak ellenére, hogy nem volt szándékában. Ha például két objektum ugyanazt a szkriptet használja ugyanazokkal a változónevekkel, akkor a fordító nem tudja kitalálni, hogy melyikre utalunk.

a probléma megelőzése érdekében a memóriában lévő összes objektum nem láthat más objektumokat. Ezután meg kell mondani egy objektumnak, hogy a szükséges változó hol van a memóriában. Az egész mutató elvet már régen elvetették, és most inkább referenciát használunk (amely valójában belsőleg használja a mutatót).

a változókhoz többféle módon lehet hozzáférni, egyesek jobbak, mint mások, mások pedig egyszerűen csak bizonyos helyzetekben használhatók.

  1. GetComponent, a leggyakoribb, szintén az egyik, hogy megzavarja a legtöbb első
  2. SendMessage, úgy tűnhet, könnyebb megérteni, de ez is kevésbé hatékony.
  3. statikus változók, a legegyszerűbb, de a legbonyolultabb, hogy teljesen megértsék az első.

jelenet beállítása

Hozzon létre egy új jelenetet, adjon hozzá egy üres játékobjektumot, és nevezze el “StormTrooper” – nek. Hozzon létre két “Health” és “Force”nevű szkriptet. Adja hozzá mindkét szkriptet a StormTrooper – hez.

nyissa meg mindkét szkriptet a szerkesztőben, és adja hozzá ezeket a sorokat:

Egészség.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; } } }}

erő.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; }}

ennek a kódnak a kipróbálása egyszerűen “objektumhivatkozás szükséges a nem statikus tagok eléréséhez”hibát ad vissza.

meg kell mondanunk a rohamosztagos egészségügyi összetevőjének, hogy hol van az erő komponens, hogy hozzáférhessen a tagjaihoz. Mivel a szkriptjeink ugyanazon az objektumon vannak, egyszerűen meg kell adnunk a szkript típusának egy változóját, amelyet el akarunk érni, és a getcomponent segítségével meg kell találnunk a szkriptet.

egy másik szkript elérése az objektumon belül

módosítsa az egészséget a következővel:

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

a módosításokkal futtassa a játékot, nyomja meg a Space gombot, majd nyomja meg a P gombot, a rohamosztagosnak most 10 egészsége van (vegye figyelembe, hogy a rohamosztagosnak nincs ereje, ez csak a példa). A változókat ugyanúgy érheti el, mint a módszereket. Csak győződjön meg arról, hogy az elérni kívánt tagok nyilvánosak-e, különben hibát kap.

Ha a tagok privátak lennének, nem lenne lehetséges hozzáférni hozzájuk. Bármi, amit nem szabad elérni az osztály/szkript kívülről, privátnak kell lennie, hogy megbirkózzon a kapszulázás elvével.

hogyan működik a GetComponent?

először deklaráljuk az elérni kívánt szkript típusának objektumát.

private Force forceScript = null;

A Force a mi típusunk, a forceScript pedig csak egy név, amelyet a szkriptben szereplő hivatkozás használatához adunk meg. Bármilyen nevet adhatunk neki.

a GetComponent függvény belenéz az objektumba (a StormTrooper játékobjektumba), hogy megtalálja az általunk átadott típusnak megfelelő összetevőt. Ha egyik sem található, akkor a null hivatkozás visszaadódik, és a forceScript változónk nem fog semmit tenni. Ha a Force típusú komponens megtalálható, akkor az adott összetevő címe átkerül a forcescript változóhoz, a változó most a memóriában tárolt Erőkomponensre mutat. Annak érdekében, hogy hozzáférjünk a Force nyilvános tagjaihoz, csak a dot operátorral kell törölnünk a szkriptváltozót. Azt mondják, hogy a forceScript változó az objektum fogantyúja. Hozzáférést biztosít az összes nyilvános taghoz (és a belső tagokhoz).

script.PublicMembers;

a privát és védett tagok elérhetetlenek maradnak.

a SerializeField attribútum lehetővé teszi, hogy a változók inspector despute hogy privát. A forceScript változó értéke nincs(erő), és erő hivatkozássá alakul. Ha rákattint, akkor kiemeli a célobjektumot.

reference_component

így tudok cache komponens getcomponent?

Igen, és néhány rendszeresen használt alkatrész esetében ajánlott.

a beépített komponensek, például a Rigidbody, Transform, Renderer,…stb.elérésekor a Unity a következő “közvetlen” hozzáférést tette lehetővé:

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

a transzformáció kivételével mindet eltávolították, mivel az adott játékobjektumon mindig van transzformáció.

a Getcomponent-et kerülni kell a frissítésen belül, így a gyorsítótárazás néhány egyszerű kódsorral ajánlott, például:

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

Ez megment néhány számítást.

szerzés sok alkatrészek

mi van, ha a rohamosztagos sok erő önmagában (nem valószínű, bár). Duplikálja az erőt kétszer, így a rohamosztagosnak három van.

az egészség válik:

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

futtassa a játékot, nyomja meg a szóközt, a Rohamosztagosunk Jedi lesz. A GetComponents megkeresi a típus összes összetevőjét, és egy tömböt ad vissza belőlük. Ez rendben van, ha nem a típus egy adott összetevőjét célozza meg, hanem valószínűbb, hogy mindegyiket.

Lásd az alábbiakat:

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

objektumok közötti interakció.

két különböző objektumot is elérhetünk hasonló módon. Hozzon létre egy új “Sith”nevű objektumot. Távolítsa el a duplikált alkatrészeket a Rohamosztagosból, így csak egy marad.

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

módosítsa a rohamosztagos erő szkriptjét úgy, hogy ne legyen ereje (a Sithek ezt gyanúsnak találnák…), adok egy nyomot, ami a Start szkriptben történik.

Az alábbiakban az a vonal található, amely megtalálja a Rohamosztagost, és megkapja annak Erőösszetevőjét.

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

GameObject.A Find() először megkeresi a paraméterként átadott objektumot, majd megkeresi benne a szükséges összetevőt. Javasoljuk, hogy ezeket a műveleteket a játék kezdetén vagy egy olyan időpontban hajtsa végre, ahol a késések nem befolyásolják a játékmenetet. GameObject.A Find and GetComponent drága függvények, mivel az egész hierarchián át kell iterálniuk. Ha a jelenet csak 10 tárgyat tartalmaz, akkor rendben lesz, de ha több ezer van benne (egy klón hadsereg), akkor biztosan rossz hatást fog látni. Fontolja meg ezeket a Start funkcióban. Ne feledje, hogy ha a jelenetben sok rohamosztagos van, az Uity nem tudja, melyiket akarta használni, és visszaadja az elsőt, amelyet talál. Valószínűleg rendelkezik hivatkozással arra az objektumra, amellyel kölcsönhatásba kíván lépni, például egy ütközési visszahívással.

interakció a hierarchián belüli objektumok között

tekintsünk egy járművet,, vezetés közben a karakter gyermek objektumként kapcsolódik hozzá. A járműnek különleges tulajdonsága lenne, ha a vezetőnek megvan az ereje (jobb kezelhetőség, gyorsabb stb.). Ahogy a vezető csatlakozik, a hierarchiában alul van.
a jármű megtalálja az erő összetevője a vezető ugyanazzal az elvvel korábban használt (GameObject.Keresés), de gyorsabbá tehetjük, ha jelezzük a járműnek, hogy a vezető a saját hierarchiájában van.

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

a GameObject átalakításra lett cserélve. Valójában egy gyermek objektum a szülő átalakulásához tartozik, ezért meg kell vizsgálnunk az átalakulást, hogy megtaláljuk tárgyunkat. A példában az objektum közvetlenül a hívó objektum alatt van, ha a Sith a pilótafülke objektumában van az autóobjektum alatt, meg kell adnia az utat:

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

a hívó objektum az autó, amelyet a hívásban ennek neveznek. Aztán átkutatják a pilótafülkét és a Sith-eket. Ha az útvonal hibás, keresse meg a NULL értéket, és kap egy Null referencia kivételt. Ezt így elkerülheti:

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

transzformáció.Find visszatér a transzformáció, míg GameObject.Keresse vissza a GameObject.

még jobb, tudjuk használni:

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

most a jármű tudja, hogy a Sith az erő és alkalmazni speciális funkció.
az egyik kérdés, hogy tudni, ha a szülő objektum tartalmaz egy script a típus, amit keres, akkor visszaadja azt. Tehát azt gondolva, hogy a gyermek komponenssel van dolgod, valójában a szülővel van dolgod.

ebben a példában minden típusú illesztőprogramot ellenőriznünk kell, hogy rohamosztagos vagy Sith-e. Jobb megközelítés lett volna az öröklődés használata, hogy a felső alaposztály megtartsa a force változót, és egyetlen funkciót használjon minden típusú meghajtóhoz. De itt nem az öröklés és a hatékonyság volt a fő kérdésünk.

SendMessage és BroadcastMessage

a Sendmessage és a BroadcastMeassage két másik lehetőség, amelyek egyszerűbbek, de drágábbak is lehetnek. Az elv ugyanaz marad, hivatkozunk egy objektumra, és módosítani akarjuk a benne lévő szkriptet.

a SendMessage segítségével meghívhat egy függvényt egy objektumon anélkül, hogy hivatkozást kellene találnia a szkriptre. Most azt mondja nekem: “mi ez a zavart út, ha egyszerűvé teheti?”

a SendMessage reflexiót használ, és rendkívül lassú a Getcomponenthez képest, és csak olyan esetekben szabad figyelembe venni, amikor ez nem befolyásolja a játékot, vagy ha nincs más megoldás.

ez logikusan hangzik, a GetComponent segítségével pontosan jelezzük, hogy a szkript hol található a memóriában. A sendmessage segítségével a fordító ismeri az objektumot, de végigmegy az összes szkripten és függvényen, amíg meg nem találja a szerencsés nyertest. Ez befolyásolhatja a játékot, ha gyakran használja több objektumon, és ha az objektumnak sok összetevője és szkriptje van (amelyek egyébként összetevők).

a SendMessage csak azt az objektumot keresi, amelyre a hívást kezdeményezik, a BroadcastMessage keresést indít a célobjektumon, de az összes gyermekén is. A kutatás még hosszabb lesz, ha az objektum nagy hierarchiát tartalmaz. A jövevény megjelent Unity4 SendMessageUpwards amit meg kellett volna kitalálni nem ugyanaz, mint BroadcastMessage de felfelé, így az összes szülő a tárgy, amíg root.

Ok nézzük meg a rohamosztagos van egy funkciója fogadására érdekében

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

most nézzük meg a rohamosztagos találkozik egy parancsnok A Birodalom:

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

mint látható, az elv ugyanaz marad, szükségem van egy hivatkozás a célobjektum, majd küldök egy üzenetet, hogy azt. Csakúgy, mint a GetComponent, jobb, ha gyorsítótárazta az objektum hivatkozását.

lehetőség van egy paraméter hozzáadására is, hogy megbizonyosodjon arról, hogy az üzenet megtalálja-e a Vevőt.

vegye figyelembe, hogy nem lehet értéket visszaadó függvényt használni. Ezután vissza kell mennie, és használnia kell a GetComponent-et.

interaktív funkciók

a Unity számos olyan funkciót használ, amelyek két vagy több objektumot kölcsönhatásba lépnek. Ezeknek a funkcióknak az az előnye, hogy sok információt tárolnak a másik objektumról.

  1. ütközési függvények OnTriggerXXXX/OnCollisionXXXX
  2. fizikai függvények XXXXcast

íme az ütközési nyilatkozat:

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

a függvény hívása létrehoz egy ütközés vagy ütköző típusú változót. A függvény kitölti ezt a példányt az ütközéssel kapcsolatos információkkal, és hivatkozást tartalmaz a másik objektumra, amellyel kölcsönhatásba lépünk.

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

Megjegyzendő, hogy nem kell keresnem a másik objektumot, mivel a colReference Collision típusú, és hivatkozást tartalmaz az adott objektumra tagként. Most a szokásos módon végrehajthatom a GetComponent-et, és hozzáférhetek a közegészségügyi változómhoz.

a Fizikai Osztály függvényei hasonló elvvel rendelkeznek. Az adatokat nem Collision/Collider példányban, hanem RaycastHit példányban tárolják:

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

kijelentem a RaycastHit típusú változót, átadom a függvénynek. RaycastHit egy struktúra, majd egy értéktípus, szükségem van az out kulcsszóra, hogy hivatkozást hozzak létre erre a struktúrára, hogy a függvény módosíthassa a struktúrát. Ha kíváncsi vagy erre a folyamatra, nézd meg a memóriakezelés cikket és az érték/referencia típus című részt.
amit itt csinálok, az az, hogy ellenőrizze, mennyire sík a felület, amelyre a kurzort helyezem. Ez hasznos stratégiai játékokhoz, ahol épületet szeretne elhelyezni, de nem sziklára.
a normál terep dot szorzatát használom a Vektorhoz3.Ha az eredmény elég közel van az 1-hez, ami azt jelenti, hogy igazodnak, a talaj elég lapos az építéshez.
nyilvánvaló, hogy további számításokra van szükség, mivel ez csak egy pontra vonatkozik, és ellenőriznie kell a normál körül, de megkapja az ötletet. Raycast segítségével interakciót hoztam létre az egér pozícióm és a terep között.

a raycast tartalmaz egy kiindulási pontot és egy irányt. Esetünkben a kiindulási pont az egér pozíciójának világpozícióvá történő átalakítása, majd a kamera forgása és vetítési mátrixa alapján egy irány. Ez a vonal végtelenül halad a meghatározott irányba (beállíthat egy maximális távolságot is), és ha ütközik egy ütközővel, akkor az ütköző és az ütközés információja a RaycastHit struktúrában tárolódik. A szerkezet ezután hivatkozást tartalmaz a találati objektumra.