Jump to content
Unity Insider Forum

Unity C#: Auf funktion aus anderen Skript zugreifen,Coins Speichern & Level


J3nsis

Recommended Posts

Hallo, ich möchte einen GameManager Skript programmieren in dem die Coins und Level gespeichert werden sollen. Wie mach ich das am besten mit mehreren Leveln  (mehrere Scenes?) Das die Coins gespeichert werden und auch wenn man ein neues Level betritt. 

Wie ich das mache Weiß ich jetzt noch nicht aber ich habe ein neues C# Script gemacht (GameManager.cs)und wollte versuchen auf die Funktion aus diesem Script namens public void newLevel() aus einem anderen Script mit GameManager.newLevel () zuzugreifen aber es funktioniert nicht. (Fehler:  NullReferenceException: Object reference not set to an instance of an object
GameManager () (at Assets/GameManagercs:22)
)

Ich habe den GameManger Script auf ein Object gemacht aber wie ist das dann wenn man ne neue Scene bzw. Level läd? Man macht Levels ja mit neuen Scenen oder? Oder ist es besser alles in eine Scene zu packen? 

 

MfG J3nsis

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Code ist ja in einer Klasse, von der ein Objekt erzeugt wird, wenn du sie auf ein GameObject ziehst. Wenn du das Skript dann noch einmal auf ein anderes GameObject ziehst, hast du schon zwei Objekte dieser Sorte. Damit du eine Funktion auf einem dieser Objekte aufrufen kannst, muss spezifiert werden, welches davon du meinst. Das kannst du auf verschiedene Arten tun.

"GameManager" klingt nach einem Fall für das Unity-Singleton-Pattern. Das erstbeste Objekt speichert eine Referenz auf sich selbst in einer statischen Variable:

public class ClassName : MonoBehaviour
{
  public static ClassName singleton { private set; get; }
  
  void Awake()
  {
    singleton = this;
  }
  
  public void Foo()
  {
    // Stuff
  }
}

Doese Referenz kann man dann benutzen, um auf dieses einzige Objekt zuzugreifen:

ClassName.singleton.Foo();

Dieses Pattern ist natürlich nur sinnvoll, wenn man nur ein einziges Objekt dieser Sorte haben wird.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ok, danke ich habe es jetzt endlich geschafft!

Aber wie meinst du das?

Zitat

Dieses Pattern ist natürlich nur sinnvoll, wenn man nur ein einziges Objekt dieser Sorte haben wird.

Ich habe ein GameManager Object und mehrere Ziele später.

Und wird der GameManager Script in einer anderen Scene immer "syncronisiert" mit dem aus der anderen Scene? (Wenn er beim Scenenwechsel nicht destroyd wird?

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 11 Stunden schrieb J3nsis:

Ich habe ein GameManager Object

Das ist es, was ich meine. Solange du nicht zwei korrekte GameManager-Objekte haben willst, passt das Pattern. Es kann halt nur eines zur Zeit in der Variable referenziert werden, nicht mehrere.

Im Moment wird das Objekt noch ganz normal zerstört, wenn die Szene gewechselt wird. Ich weiß, das ist jetzt vielleicht etwas verwirrend, weil ich oben geschrieben habe, dass eine statische Variable dagegen hilft. Ich erklär das mal.

Unity hat eine Funktion, die vor finsterer Magie nur so tropft: Destroy. Wenn man ein Unity-Objekt (z.B. ein GameObject oder eine Komponente) mit Destroy zerstören lässt, dann wird sie am Ende des Frames gelöscht. Wenn man dann im nächsten Frame den Wert einer Variablen abfragt, die dieses Objekt referenziert, ist der Wert "null", also "nichts referenziert".

var light = GetComponent<Light>(); // Existiert, gibt also nicht null zurück
Destroy(light);
// Warte einen Frame
if(light == null)
{
  Debug.Log("Jap, ist weg");
}

Tatasache ist, dass das in C# eigentlich schlicht unmöglich ist. Also, dass der Wert der Variablen sich geändert hat. Wie die bei Unity das gemacht haben, ist erst einmal aber nicht wichtig. Wichtig ist hier, dass eine Variable, die ein Objekt referenziert, das mit Destroy gelöscht wird, plötzlich den Wert "null" bekommt, sobald die Zerstörung fertig ist. So ist es auch bei deiner statischen Variable im Pattern.

Alles, was ein Szenenwechsel macht, ist alle Objekte der aktuellen Szene mit Destroy zu löschen und alle Objekte der neuen Szene zu instanziieren. Dabei werden alle Komponenten auf den GameObjects natürlich ebenso gelöscht wie die GameObjects selbst, und damit gehen alle Werte von Variablen flöten, die die Komponenten hatten. Der Kern hinter statischen Variablen ist aber, dass sie nicht an die Objekte (hier: Komponenten) gebunden sind, sondern genau wie die Klasse selbst "übergreifend" existieren. Deshalb kannst du auch sagen "Klassenname.variable", weil es diese Variable nicht einmal pro Objekt gibt (light1 hat eine andere Farbe als light2), sondern einmal pro Klasse, und damit auch genau wie die Klasse unsterblich ist. Naja, bis das Programm selbst beendet wird.

Deshalb kannst du in statischen Variablen Werte unterbringen, die einen Szenenwechsel überleben - sie sind nicht an die Komponenten gebunden, die da massenweise gelöscht werden.

public static int score;

Allerdings haben wir hier den Fall, dass das Objekt, das deine statische Variable referenziert, mit Destroy gelöscht wird. Obwohl der Szenenwechsel also die Variable nicht direkt beeinflusst, so wird Destroy dennoch für den Wert "null" sorgen, sobald es das referenzierte Objekt löscht. Der GameManager ist also weg und die statische Variable hat dann "null" als Wert.

Lange Rede (die hoffentlich beim Verständnis hilft), kurzer Sinn: Du musst deinen GameManager immer noch vor der Zerstörung beim Szenenwechsel bewahren, und das geht mit DontDestroyOnLoad. Es passt sehr gut in's bereits vorhandene Pattern:

public class ClassName : MonoBehaviour
{
  public static ClassName singleton { private set; get; }
  
  void Awake()
  {
    if(singleton == null)
    {
      singleton = this;
      DontDestroyOnLoad(this);
    }
    else
    {
      Destroy(gameObject);
    }
  }
}

Beachte, dass hier eine Abfrage dazu gekommen ist. Wenn du deinen GameManager z.B. im Hauptmenü hast und zum zweiten Mal im Hauptmenü vorbei schaust, dann ist es vielleicht egal, dass singleton den neuen GameManager referenziert. Aber du willst nicht, dass zwei GameManager-Objekte existieren, die beide durch DontDestroyOnLoad vor Szenenwechsel geschützt sind. Dann werden das nämlich immer mehr :)

Daher setzt sich das Ding nur dann als "das" Ding, wenn es noch keines gibt. also singleton den Wert "null" hat. Ansonsten zerstört es sein eigenes GameObject sofort selbst. Damit sollte sich auch deine letzte Frage geklärt haben.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...