Jump to content
Unity Insider Forum

GameObject.Find


Kojote

Recommended Posts

Grüße! :)

Kleine peinliche Frage zu GameObject.Find.

Ich habe folgende Variable:

private ButtonAktualisierer kameraButtonOben;

Im Awake, möchte ich gerne eine Verbindung zu dem Objekt bzw. zu einer seiner Komponenten herstellen, dem Script Buttonaktualisierer:

kameraButtonOben = GameObject.Find("/Canvas/Kamera oben").GetComponent<ButtonAktualisierer>();

Problem ist nun folgendes:

NullReferenceException: Object reference not set to an instance of an object
Kamerasteuerung.Awake () (at Assets/Scripts/Kamerasteuerung.cs:44)

Kann mir das jemand erklären? Wenn ich normal nach Objekten suche, findet er sie auch und alles gut. Will ich jedoch auf eine bestimmte Komponente zugreifen, wie dem Script ButtonAktualisierer, bekomm ich einen Fehler.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hol die erst die Objektreferenz und dann die Komponente, dann kannst du auch besser unterscheiden, was er nicht findet:

GameObject m_kameraButtonOben = GameObject.Find("/Canvas/Kamera oben");
if (m_kameraButtonOben!=null) {
  ButtonAktualisierer m_buttonAktualisierer = m_kameraButtonOben.GetComponent<ButtonAktualisierer>();
  if (m_buttonAktualisierer==null) Debug.LogError("Skript 'ButtonAktualisierer' nicht gefunden!");
} else {
  Debug.LogError("Objekt '/Canvas/Kamera oben' nicht gefunden!");
}

Ich denke er findet bei dir das Objekt nicht, da evtl. der Pfad falsch ist oder es nicht aktiv ist.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 43 Minuten schrieb Zer0Cool:

Ich denke er findet bei dir das Objekt nicht, da evtl. der Pfad falsch ist oder es nicht aktiv ist.

Würde es hierbei Probleme geben? Es ist der Fall, dass ich beim Start das Object deaktiviert. Deswegen wird er es auch nicht finden. Kann man es finden, auch wenn es deaktiviert ist?

EDIT:

Was etwas blöd ist, merk ich gerade, ich lege einmal im Awake und Start die Verbindung zu dem Objekt fest. Wenn ich eine Ebene darüber, dass Objekt deaktiviere, findet er alle weiteren Objekte nicht mehr...

Link zu diesem Kommentar
Auf anderen Seiten teilen

Und damit bist du an den Punkt gestoßen, wegen dem in Awake die Referenzen gesetzt werden und in Start die erste Logik passiert :)

Alle Awakes sind vor allen Starts. Wenn du also in Awake GameObject.Find machst und in Start deaktivierst, sollte das funktionieren. Dann hast du "nur noch" das Problem, dass GameObject.Find trotzdem stark fehleranfällig ist. Mal vertippt, mal den Namen eines Objekts geändert und schon funktioniert dein Spiel nicht mehr. Leute sind immer genervt, wenn ich das sage, aber wenn's nach mir geht, sollte man in 99,9% aller Fälle von GameObject.Find komplett die Finger lassen. Es gibt immer alternative Wege.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Die Theorie ist das eine (Aufrufreihenfolge und Initialisierungszustände der Klassen und ihrer Instanzen zum Zeitpunkt von "Awake" und "Start"), ich habe allerdings eher "schlechte" Erfahrungen mit "Awake" gemacht und musste schon oft Initialisierungscode in die Startmethode hinein verschieben, um einen Fehler zu beheben, leider waren diese Fehler nur sehr schwer zu finden und oft auch nicht logisch zu erklären und auch Code von Assets die man kauft sind oft noch mit solchen Fehlern "gepflastert" (diese Fehler passieren dann eher "zufällig", da sie von der zufälligen Abarbeitung der Skripte abhängen) . Als "Daumenregel" würde ich sagen, das man nur einfachen Lowlevel-Initialisierungscode (Code mit wenigen Abhängigkeiten zu anderen Klassen oder Instanzen) in die Awake-Methode packen sollte, alle "höheren Funktionen" lieber in die Startmethode. Dabei zählen zu höheren Funktionen auch Unityeigene Systemfunktionen, bei denen Eigenschaften eingestellt werden.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja, das stimmt, aber auch mit Referenzen zu externen Objekten oder Komponenten in Awake wäre ich vorsichtig. Nur als Beispiel, oft werden Objekte in Awake- oder Start-Methoden aktiviert und wenn man in einer anderen Awake-Methode danach sucht, so wird das gesuchte Objekt ggf. nicht gefunden (hängt von der zufälligen Ausführungsreihenfolge ab), da Objekt.Find nur Objekte findet, die bereits aktiviert sind... Gleiches gilt auch für Komponenten, diese werden oftmals deaktiviert und wenn man in der Awakemethode versucht eine Liste mit Referenzen zu solchen Komponenten aufzubauen, so werden sie ggf. nicht gefunden (hier hängt es von der Suchmethode ab, es gibt eine Suchmethode, die auch deaktivierte Komponenten findet)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Klar - ist eher eine Faustregel. Es gibt halt ohne ein fünfjähriges, tiefgreifendes Studium der Informatik eigentlich keine Themen, bei denen man nicht bei jeder Aussage "prinzipiell" voranstellen und "aber da gibt es Ausnahmen" hinterherwerfen müsste :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

11 hours ago, Zer0Cool said:

Ja, das stimmt, aber auch mit Referenzen zu externen Objekten oder Komponenten in Awake wäre ich vorsichtig. Nur als Beispiel, oft werden Objekte in Awake- oder Start-Methoden aktiviert und wenn man in einer anderen Awake-Methode danach sucht, so wird das gesuchte Objekt ggf. nicht gefunden (hängt von der zufälligen Ausführungsreihenfolge ab), da Objekt.Find nur Objekte findet, die bereits aktiviert sind... Gleiches gilt auch für Komponenten, diese werden oftmals deaktiviert und wenn man in der Awakemethode versucht eine Liste mit Referenzen zu solchen Komponenten aufzubauen, so werden sie ggf. nicht gefunden (hier hängt es von der Suchmethode ab, es gibt eine Suchmethode, die auch deaktivierte Komponenten findet)

Deswegen kann man ja sagen, dass das eine Skript welches denn unbedingt nach anderen Objekten suchen muss, nach allen anderen ausgeführt wird. Siehe mein Post oben.

Damit kann man die Initialisierung immernoch im Awake machen statt im Start, oder nicht?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Leider sind es oft mehrere Skripte die nach externen Referenzen suchen. Ich würde mal sagen, bei Initialisierungen für das eigene Objekt (da man den Zustand ja kennt) ist Awake am besten geeignet, hier ist es dann im Endeffekt eine Art Standardkonstruktor für die MonoBehaviour-Klasse. Für Referenzen auf andere Objekte nur wenn man dessen Zustand (und die Auswirkungen auf einen Zugriff auf diese Objekte in der Awake-Methode) genau kennt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 6 months later...

Hallo,

Am 13.6.2017 um 16:12 schrieb Zer0Cool:

Hol die erst die Objektreferenz und dann die Komponente, dann kannst du auch besser unterscheiden, was er nicht findet:

Danke für diesen Satz.

Folgendes klappte bei mir nicht (Wenn mein Player zerstört wurde und kurz darauf ein Prefab mittels Instantiate und dem unten stehenden Script erstellt wurde erhielt ich immer die Fehlermeldung: "NullReferenceException: Object reference not set to an instance of an object"):

private Transform ply;
    public float moveSpeed = 0.1f;

    void Start()
    {
        ply = GameObject.Find("Player").transform;
    }

    void FixedUpdate()
    {
        if (ply)
        {
            transform.LookAt(ply);
        }

        transform.Translate(Vector3.forward * moveSpeed);
    }

In der Fehlermeldung wurde auf die Zeile in der Start-Funktion verwiesen, in der ich den Player (oder besser gesagt seine Transform-Komponete) suchte. So hatte ich es in verschiedenen YouTube Tutorials gesehen. Aber wie oben geschrieben: es lief nicht richtig wenn der Player schon weg war.

Folgendes klappte:
 

private GameObject ply;
    public float moveSpeed = 0.1f;

    void Start()
    {
        ply = GameObject.Find("Player");
    }

    void FixedUpdate()
    {
        if (ply)
        {
            transform.LookAt(ply.transform);
        }

        transform.Translate(Vector3.forward * moveSpeed);
    }

Hier habe ich nun erst den Player gesucht und später in der FixedUpdate-Funktion die Transform-Komponente ausgewählt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Warum der erste Code bei dir nicht klappte, liegt daran, dass du ply zwar als Transform deklarierst, aber du ja nicht nach der Transformkomponente gesucht hast.
Du hast da lediglich nach dem ganzen Gameobjekt gesucht und versucht das "Achtung" -kleingeschreibenes-  transform zu verwenden. Das ist eine interne Referenz, aber nicht die Komponente!
Das geht natürlich so nicht.
Wenn du die Transformkomponente direkt referenzieren willst, dann musst du das so machen:
 

ply=GameObject.Find("Player").GetComponent<Transform>();

Also erst das Gameobject finden und dann die Komponente des Gameobjects holen.

In deinem 2ten Beispiel funktioniert das ohne die Komponente zu holen, weil du da ein GameObject suchst und der ply Variable übergibst. Das ist jetzt zwar nur ein GameObject, aber da ein GameObject nicht ohne Transform-Komponente existieren kann, wird automatisch von Unity eine Referenz gebildet und die kannst du jetzt über das kleingeschrieben transform erreichen.
Unity macht das nur noch mit der Transformkomponente und dem GameObject welches dann gameObject heisst.

In der Version 3 und evtl. sogar noch der Version 4, konntest du z.B. auch einen Rigidbody mit dem kleingeschriebenen rigidbody ansprechen und auch andere Dinge waren automatisiert.
Das alles geht jetzt nicht mehr automatisch und du musst jetzt alle Komponenten, die du von einem GameObject ansprechen willst, selber referenzieren. So, wie ich es oben im Beispiel code geschrieben habe.
 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...