Jump to content
Unity Insider Forum

Eine Variable aus einem anderen Script aufrufen


Daykoo

Recommended Posts

Guten Tag an alle!

Ich stehe vor folgendem Problem:
Ich habe ein Script (Klassenwahl), das mehrere Variablen beinhaltet. 

    public bool dieb_state = false;
    public bool krieger_state = false;
    public bool magier_state = false;

Sobald in dieser Scene auf das jeweilige Objekt geklickt wird, soll der dazugehörige Status (_state) auf true gesetzt werden. 

    public void OnMouseDown() {
        if (gameObject.tag == "Dieb") {
            krieger_state = true;
            SceneManager.LoadScene("Welt_01");
            Debug.Log("Den Dieb berührt!");
        } 
        else if (gameObject.tag == "Krieger") {
            krieger_state = true;
            SceneManager.LoadScene("Welt_01");
            Debug.Log("Den Krieger berührt!");
        }
        else if (gameObject.tag == "Magier") {
            magier_state = true;
            SceneManager.LoadScene("Welt_01");
            Debug.Log("Den Magier berührt!");
        }
        else {
        }
    }

Nun ist das zweite Script (CharSpawn) dran:
Natürlich ist Variable deklariert um auf das Script (Klassenwahl) zugreifen zu können.

    public Klassenwahl state;

Sobald die Scene gestartet wird, also bei void Start(), habe ich diesen Code:

    public Klassenwahl state;

    public GameObject krieger;
    public GameObject dieb;
    public GameObject magier;

    void Start() {
        if (state.dieb_state == true) {
            Instantiate(dieb, new Vector3(0, 1, 5), Quaternion.identity);
        }
        else if (state.krieger_state == true) {
            Instantiate(krieger, new Vector3(0, 1, 5), Quaternion.identity);
        }
        else if (state.dieb_state == true) {
            Instantiate(magier, new Vector3(0, 1, 5), Quaternion.identity);
        }
        else {

        }
    }

Die jeweiligen Models sind im Editor hinzugefügt.

Desweiteren sind beide Scripts in den Scenes vorhanden. Das erste auf den Charakteren und das zweite als EmptyGaOb in der Scene in der der Char "erscheinen" soll.
Die Variable State beinhaltet ein GaOb mit dem Script Klassenwahl.

 

Nur zur eigentlichen Frage:
Warum werden die Modells nicht gespawnt? Ich bitte um Lösungsvorschläge. 

 

Schon einmal großen Dank im voraus

Daykoo

 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn du eine Szene lädst, dann wird die alte Szene entladen (alle Objekte darin werden gelöscht) und die neue Szene wird danach geladen (alle Objekte darin werden instanziiert). Wenn du ein Objekt in Szene 1 hast und dann Szene 2 lädst, dann sind alle Objekte weg, die in Szene 1 waren. Dass du jetzt ein GameObject mit der gleichen Komponente darauf auch in Szene 2 hast, bedeutet nicht, dass es sich um dasselbe Objekt handelt - es ist ein neues Objekt derselben Art. Also das gleiche Objekt, nicht dasselbe. Entsprechend sind alle Einstellungen, die du am Objekt in Szene 1 vorgenommen hast, weg.

Ich könnte jetzt mit dieser oder jener Lösung anfangen, aber hier gibt es noch einige andere Probleme. Ich erkläre mal, wo es hakt, und dann zeige ich darauf aufbauend eine mögliche Lösung.

Fangen wir mit den drei Booleans an.

Beim Programmieren können Fehler passieren. Wir sind halt Menschen und Menschen machen Fehler. Darum ist es gut, sich bestimmte Techniken anzugewöhnen, um "guten Code" zu schreiben. Guter Code tut mehr, als einfach nur zu funktioneren. Er minimiert zum Beispiel das Risiko, Fehler zu machen.

Ein Spieler wählt eine Klasse aus. Er hat immer nur eine. Das heißt, dass es sinnvoll wäre, dass das Programm auch nur eine Klasse zur Zeit zulässt. Wenn du allerdings drei Booleans hast, dann ist es theoretisch möglich, dass zwei oder mehr davon true sind, und der Zustand deines Programms ergibt keinen Sinn mehr. Dass das möglich ist, bedeutet direkt Fehlerpotential, denn du musst zu jedem Zeitpunkt darauf aufpassen, dass das nicht passiert. Und je mehr es gibt, auf das du ständig achten musst, um keine Fehler zu machen, desto unmöglicher wird es, keine Fehler zu machen.

Stattdessen wäre ein enum möglich, aber ich zeige gleich lieber eine andere Lösung.

Das nächste Problem wären Strings. In diesem Fall benutzt du Tags.

Ich schicke eines gleich voran: In meinen Projekten benutze ich niemals Tags. In jeder Situation, in der man sie gebrauchen kann, gibt es jeweils mindestens eine bessere Lösung. Trotzdem kann man sie erst einmal benutzen. Problem ist halt, dass man sich bei Strings immer vertippen kann oder sie sich ändern, ohne dass einem der Compiler sagt, dass etwas nicht stimmt. Wenn du im Code "Krieger" falsch schreibst, dann siehst du erst einmal nur einen Fehler im Test und musst danach im Code suchen, anstatt dass der Computer das für dich übernimmt.

Dann kommt noch die if-else-Kette.

Du hast jetzt eine Klasse, die nach dem Tag ihres GameObjects schaut und ein Boolean auf true setzt. Eine andere Klasse hat für jeden dieser Booleans eine if-Abfrage und ein Prefab, das gegebenenfalls instanziiert wird. Das heißt: Wenn du n Klassen hast, hast du n Tags, n Booleans und n if-Abfragen. Jedes Mal, wenn du eine Klasse in dein Spiel hinzufügst, musst du also einen Tag hinzufügen, ein Boolean in die eine Klasse einbauen und eine if-Abfrage in die andere. Das sind drei Arbeitsschritte für eine einzelne Änderung an deinem Spiel. Das klingt vielleicht nicht nach viel, aber wenn du einen dieser Schritte vergisst, hast du gleich wieder einen Fehler gebaut - und zwar wieder einen, bei dem dir der Compiler nicht helfen kann. Bei komplexeren Systemen gibt es dann noch mehr Arbeitsschritte pro Änderung, sodass das Fehlerpotential immer weiter steigt. Es sei denn, man geht grundlegend anders an die Sache heran.

Nun zu einem Lösungsvorschlag.

Was du letztenendes tust ist, dass du mehrere Objekte hast und jedes dieser Objekte steht für jeweils ein Prefab, das nach dem Laden immer derselben Szene instanziiert wird.

Also Knopf -> Prefab, denn jeder Knopf steht für ein Prefab.

Aktuell haben wir aber Knopf -> Boolean -> if-Abfrage -> Prefab, und wir können den gesamten Zwischenweg einfach rausnhemen. Wir ändern die Klassen wie folgt ab:

Deine Knopf-Klasse, auf die man drückt, erhält eine Referenz auf das Prefab, für das es steht:

public GameObject classPrefab;

Anstatt jetzt den Tag des GameObjects einzustellen, ziehst du auf jeden deiner Klassen-Auswahlknöpfe das jeweils passende Prefab.

Als nächstes nehmen wir uns das Script vor, das in Szene 2 dieses Prefab instanziieren soll. Es kriegt ebenfalls ein Feld für ein Prefab und spawnt dieses einfach nur:

public GameObject prefabToSpawn;

private void Start()
{
  Instantiate(prefabToSpawn, transform.position, transform.rotation);
}

Ich habe für Position und Rotation hier die Position und Rotation des GameObejcts genommen, auf die du das Spawner-Script ziehst. Dann kannst du deinen Spawner in der Szene platzieren, um zu entscheiden, wo man spawnt, anstatt dafür den Code ändern zu müssen.

Jetzt haben wir zwei sehr kleine Klassen. Wir brauchen nur noch eine OnMouseDown-Methode in der ersten, die das eigene Prefab als "prefabToSpawn" der zweiten Klasse angibt und die richtige Szene lädt. Du siehst vielleicht bereits die Vorteile der ganzen Sache.

Jetzt haben wir ein Problem, und zwar dasselbe wie vorher auch schon. Das Objekt, dem wir etwas mitteilen wollen (nämlich das mit der Spawner-Klasse darauf in der zweiten Szene) existiert noch gar nicht zu dem Zeitpunkt, wo wir in Szene 1 auf den Knopf klicken. Hier gibt es mehrere Lösungen, wie man eine Information an einem Ort hinterlegt, der nicht zusammen mit der Szene gelöscht wird. Ich zeige dir mal die einfachste, auch wenn es andere mit zusätzlichen Vorteilen gibt.

Das Schlüsselwort static wird oft missverstanden, sei also vorsichtig, wenn du selber noch einmal danach suchst. Ich erkläre mal:

Wenn du ein Feld anlegst, also eine Variable außerhalb einer Methode, dann gehört sie zum Zustand des Objekts. So hat z.B. Unitys "Light"-Komponente ein Feld "color", das die Farbe des Lichts angibt. Wenn du zwei Lichter hast, dann kannst du das eine rot und das andere blau machen, denn "color" gehört zum Objekt.

Wenn du ein Feld allerdings als "static" markierst, dann gehört das Feld nicht mehr zum Objekt, sondern zur Klasse selbst. Wäre "color" als "static" markiert, dann würden alle Lichter immer dieselbe Farbe haben.

Dadurch, dass statische Felder eben nicht zum Objekt gehören, gehen ihre Werte bei einem Szenenwechsel nicht verloren; denn das passiert ja dadurch, dass die Objekte gelöscht werden.

Wir ändern deshalb die erste zeile des Spawner-Scripts ab und fügen ein "static" ein:

public static GameObject prefabToSpawn;

private void Start()
{
  Instantiate(prefabToSpawn, transform.position, transform.rotation);
}

Wenn wir die Farbe eines Lichts ändern wollen, dann müssen wir definieren, welches der vielen Lichter wir überhaupt meinen. Sonst weiß Unity ja nicht, welches Licht ab jetzt weiß leuchten soll. Das macht man über die Punktnotation, also "irgendeinLicht.color = Color.white;". Da statische Variablen eben nicht mehr zu den Objekten gehören, sondern nur noch einmalig Teil der Klasse sind, benutzt man die Punktnotation zusammen mit dem Klassennamen. Nehmen wir also an, das Script mit der statischen Variable heißt "PlayerSpawn", dann müssen wir schreiben:

PlayerSpawn.prefabToSpawn = irgendeinPrefab;

und genau das machen wir jetzt in OnMouseDown der Klassenauswahl-Knopf-Klasse:

public GameObject classPrefab;

private void OnMouseDown()
{
  PlayerSpawn.prefabToSpawn = classPrefab;
  SceneManager.LoadScene("Welt_01");
}

Wenn du also auf dieses Objekt klickst, wird das Prefab, das du beim jeweiligen Knopf im Inspektor reingezogen hast, in der PlayerSpawn-Klasse registriert. Wenn dann der Szenenwechsel passiert, hat das Feld "prefabToSpawn" immer noch dieses Prefab referenziert und die Start-Methode greift darauf zu.

Das alles in wenigen Zeilen Code, ohne Strings, ohne Tags, und wenn du eine weitere Klasse zu deinem Spiel hinzufügen willst, dann baust du einen neuen Button und ziehst das Prefab der neuen Klasse hinein und bist fertig.

Ich hoffe, das hilft dir weiter, vielleicht nicht nur bei diesem einen Problem ;)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...