Jump to content
Unity Insider Forum

Wiederverwendung in zweiter Szene


chrische5

Recommended Posts

Hallo

Ich habe jetzt den ersten Level meines Spiels so fertig, dass ich gern mit dem zweiten arbeiten will. Nun kommt eine Frage, die wahrscheinlich trivial erscheint, wenn man den workaround kennt, mir jedoch noch nicht ganz klar ist. Ich habe eine Dinge in der Hierachy, die ich natürlich auch in der zweiten Szene nutzen möchte (UI, Hauptcharakter, einige Listener,...). Wie verfährt man dabei üblicherweise? Macht man die entsprechenden Sachen zu Prefabs und zieht sie einfach erneut in die Szene? Und/Oder abreite man mit DontDestroyOnLoad()? Das habe ich noch nie genutzt? Wie sieht das dann konkret aus? Bleiben die entsprechenden GO einfach in der Hierachy? 

 

Danke für eure Antworten 

Christoph   

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 12 Stunden schrieb chrische5:

Macht man die entsprechenden Sachen zu Prefabs und zieht sie einfach erneut in die Szene? Und/Oder abreite man mit DontDestroyOnLoad()?

Kommt drauf an!

Ich weiß, lästige Antwort, aber doch so oft die richtige :)

Mit Prefabs kannst du erstmal nicht so viel falsch machen. Gerade, wenn man Nested Prefabs nutzt, wird's wirklich simpel. Da kannst du alle deine wichtigen Prefabs einfach in ein Prefab nesten und nur dieses eine reinziehen.

DontDestroyOnLoad zu nutzen würde erstmal (!) bedeuten, dass du deine wichtigen GameObjects in einer oder mehreren Szenen hast und diese Szene(n) zu irgendeinem frühen Zeitpunkt einmal geladen sind. Dann flagst du deine Objekte mit DontDestroyOnLoad und ja, sie verschwinden dann nicht mehr beim Szenenwechsel. Früher blieben die in der Tat einfach in der Hierarchie. Heute macht Unity, zumindest im Editor, eine neue "DontDestroyOnLoad-Szene" auf, in der alle geflagten Objekte wohnen. Diese Szene wird nicht entladen, wenn man eine neue Szene nicht-additiv lädt. Ob das im Build auch so ist, hab ich nie getestet :)

Diese Herangehensweise mit DontDestroyOnLoad habe ich immer mal wieder empfohlen gesehen (im Sinne von "mach dir eine 'wichtige Objekte'-Szene und lade die als erstes"), aber ich finde sie sehr problematisch. Nehmen wir mal ein Pausemenü-UI-Prefab. Das willst du ja in jedem Level gleichermaßen haben... außer im Hauptmenü. Du musst also irgendwie dafür sorgen, dass das Ding in der Hauptmenü-Szene deaktiviert ist und sich ansonsten wieder re-aktiviert. Da habe ich schon öfter mal sowas wie

if (geladeneSzene.name == "main menu")

gesehen. Fürchterlich! Da musst du einfach immer im Hinterkopf behalten, dass du die Hauptmenü-Szene nie umbenennen darfst. Als Solo-Dev vielleicht noch erträglich, aber sollte man sich nicht immer Techniken angewöhnen, die auch im Team funktionieren? Und was, wenn es mal mehr Szenen (Optionen? Credits? Multiplayer-Lobby?) gibt, die auch kein Pausemenü haben sollen?

Du musst darüber hinaus sicherstellen, dass diese spezielle Szene einmal geladen wird, bevor irgendetwas anderes passiert. Gibt also vermutlich eine weitere Einschränkung für die Build List. Und was, wenn du im Editor eine Szene testen willst? Da kannst du dir Editor-Code schreiben, der diese Szene immer einmal lädt, sobald der Play Mode gestartet wird. Wieder etwas, das man irgendwie schon machen kann, aber... worauf ich hinaus will ist: Meiner Meinung nach sollte man Einschränkungen darin, was man im Editor machen darf, vermeiden. Und die Komplexität und Menge der Arbeitsschritte, die es braucht, um etwas zu implementieren, gering halten. Dieser ganze Ansatz ist unter diesem Gesichtspunkt eine riesige Katastrophe. Für mich ist der Unity Editor der Ort, an dem Gamedesign passieren soll. Je mehr Gamedesign im Code passiert, und je mehr verpflichtendes Setup im Editor passiert, damit der technische Code richtig funktioniert, desto schlechter ist die Architektur demnach.

Jetzt darfst du das natürlich anders sehen, aber für den Fall dass du da zustimmst, hier eine andere Variante: Du kannst den Code selber Dinge instanziieren lassen. Hier ein Beispiel:

public static class CoroutineService
{
  private class Worker : MonoBehaviour { }
  
  private static Worker worker;
  
  [RuntimeInitializeOnLoadMethod]
  private static void Initialize()
  {
    var go = new GameObject("Coroutine Service");
    go.hideFlags = HideFlags.HideAndDontSave;
    Object.DontDestroyOnLoad(go);
    worker = go.AddComponent<Worker>();
  }
  
  public static Coroutine StartCoroutine(IEnumerator coroutine)
  {
    return worker.StartCoroutine(coroutine);
  }
  
  public static void StopCoroutine(Coroutine coroutine)
  {
    worker.StopCoroutine(coroutine);
  }
}

Du siehst eine statische Klasse, die ein GameObject bei Spielstart erzeugt. Das Attribut [RuntimeInitializeOnLoadMethod] ist pures Gold. Du packst es über eine beliebige statische Methode und diese wird direkt beim Spielstart, noch vor irgendwelchen Awakes oder Starts der ersten Szene, ausgeführt. Das ist dann wie bei der speziellen Szene, die ich weiter oben meinte - nur halt nicht im Editor, sondern im Code.

Der Reihe nach:

  • new GameObject ist klar.
  • HideFlags.HideAndDontSave sorgen dafür, dass das Objekt nicht in der Hierarchie auftaucht und auch nie irgendwie in der Szene gespeichert wird. So kann man weniger versehentlich kaputt machen.
  • DontDestroyOnLoad ist jetzt auch klar: Das Objekt bleibt während des gesamten Spiels verfügbar und ist nicht beim ersten Szenenwechsel wieder weg.
  • AddComponent ist auch klar - eine Referenz auf die erzeugte Komponente wird gemerkt.

Die anderen beiden Methoden erlauben dir jetzt einfach, die statische Klasse um das Ausführen/Stoppen einer Coroutine zu bitten, und das Arbeiter-GameObject, das komplett von dieser Klasse verwaltet wird und im Editor komplett unsichtbar ist, führt das dann aus.

CoroutineService.StartCoroutine(MyCoroutine());

Das ist echt super für technische Sachen. Aber so Sachen wie "in welchen Szenen gibt es ein Pause-Menü?" sind einfach nicht technisch. Das ist 100% Gamedesign. Und deshalb würde ich für das Pause-Menü hiervon abraten und ganz schlicht über das Reinziehen eines Prefabs definieren, dass diese Szene ein Pause-Menü zu haben hat.

Einen Nachteil hat mein Code da oben noch: Was ist, wenn du ein global verfügbares Objekt haben, aber etwas im Editor einstellen oder referenzieren willst? Statt eines CoroutineService, was wäre mit einem BackgroundMusicService? Eine AudioSource, die immer da ist und der man neue Musik zum Spielen geben kann. So eine AudioSource hat einiges an Einstellungen, die man im Editor machen kann und will. Spatial Blend, Volume, und nicht zuletzt Audio Mixer Group. Und darauf habe ich zum Glück auch eine Antwort, da ich weiß, dass du Soda besitzt: Dafür ist das ModuleSettings-System da :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo

 

Wow!Wow!Wow! Was für eine Antwort. Ich habe es bereits mehrfach geschrieben: Deine Art zu antworten, ist wirklich gut! Als Pädagoge bilde ich mir sogar ein, das etwas einschätzen zu können. Ich ehrlicherweise überlegt, ob ich Google anwerfe oder hier nachfrage. Da die Antworten hier meist sehr kompetent sind, habe ich mich für das Forum entschieden und wurde mal wieder bestätigt.

 

Christoph

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...