Jump to content
Unity Insider Forum

Elegantere Lösung für einen Restart? - GELÖST


Der_Stefan_

Recommended Posts

Ich mal wieder... wieder mal ein Problem :D bzw. eigentlich... kein Problem. Ich bin auf der Suche nach einer etwas charmanteren Lösung als das grobe Holzgehacke, welches ich hier fabriziert habe. 


Und zwar baue ich ja grad diesemn - Ich nenne es jetzt einfach mal "Endlessrunner", bei dem ich mit einem immer schneller werdenden Raumschiff durch sich drehende Schotts fliegen muss. Zu den sich bewegenden Segmenten habe ich ja kürzlich auch schon mal eine Frage gestellt (siehe Verlinkung unten). Kurz zusammengefasst: Am Ende jeden Röhrensegments befindet sich ein Trigger, der das Segment nach vorne springen lässt. Das ganze mit 4 Röhrensegmenten, so dass man immer mindestens 3 Segmente vor der Nase hat. Das gleiche mit den Schotts am Ende jeden Röhrensegments. Ich bin nun mittlerweile auch schon nahezu am Ende des Projektes, es funktioniert alles so weit so gut, das Spiel ist quasi bereits im vollen Umfang spielbar.

Aaaaber: Am Ende, wenn man schlussendlich doch gegen eine Wand gedonnert ist, habe ich einen Restart-Button eingebaut, bei dem sich Raumschiff, Rohr-Segmente und Schotts auf ihre Ursprungsposition setzen. Wie gesagt, etwas hölzern habe ich das wie folgt umgesetzt: Auf jedes der 4 Rohr-Segmente und der 4 Schotts habe ich ein "Restart"-Script gesetzt (Auszug):

 

    void Awake()
    {
        this.originalPosition = this.transform.position;
        this.originalRotation = this.transform.rotation;
    }
    
    public void Menurestart_Click()
    {
        this.transform.position = this.originalPosition;
        this.transform.rotation = this.originalRotation;

Für die Menüs und Menübuttons habe ich eigentlich ein eigenes Script, aber ich habe es nicht hinbekommen, die 8 GameObjects aus diesem Script heraus anzusprechen. Ich musste also jedes Object mit einem eigenen Script für den Restart versorgen und dann alle GameObjects einzeln in der Funktion des Buttons hinterlegen. 9x mit dem Raumschiff. Dazu in jedes Gameobject alle relevanten UI-Buttons in die Public-Slots (siehe Screenshots im Anhang). 

Wie gesagt: Es funktioniert. Aber geht das nicht irgendwie eleganter? Kann ich nicht zum Beispiel oben genanntes Script-Segment in mein Menü-Funktionsscript übernehmen und z.B. statt "this" auf einen Tag des GameObjects verweisen?

Oder, ohne Tags, sowas wie 

GameObject.buttonRestart
{
this.transform.position = this.originalPosition;
}

oder 

{
GameObject.buttonRestart.transform.position = GameObject.buttonRestart.originalPosition;
}

Funktioniert natürlich beides nicht (und ja, ich hab`s wider besseren Wissens probiert :D )

Ich habe in den Weiten des Internets bereits diverse Lösungsansätze dazu gefunden. Die für mich logischsten/ verständlichsten Ansätze waren das Menü als eigene Szene aufsetzen und per Script einfach die Level-Szene neu laden. Habe ich erst gar nicht ausprobiert, mein Menü steht ja jetzt schon und... Näää :D Beim nächsten mal und dann direkt von vornherein. Und als zweite Möglichkeit, wäre es, aus dem Menü-Script heraus eine "Message" an die jeweiligen Scripte in den GameObjects zu senden. So hätte ich zwar immer noch einzelne Scripts, aber ich müsste den Button nur "einmal" verlinken. Klingt gut, aber ich habe es einfach nicht zum laufen bekommen...

Wie würdet ihr das lösen?

 

Danke für euer Feedback und möge euch nie die Sonne auf den Bildschirm scheinen :)

 

Hier dann noch die besagte Verlinkung der Frage zu den sich bewegenden Segmenten (Der vollständigkeitshalber)

 

 

Snip1.JPG

 

Snip3.JPG

Snip4.JPG

Snip5.JPG

Link zu diesem Kommentar
Auf anderen Seiten teilen

Die simpelste Variante wäre, die Szene einfach neu zu laden.

Wenn du das nicht machen willst - sei es, weil du z.B. inakzeptable Ladezeiten feststellst, aber auch, weil du etwas anderes probieren willst - dann kannst du den Spieß umdrehen. Anstatt per Hand die Objekte in einer Liste zu haben - dass also dein zentraler Knotenpunkt die ganzen Objekte von sich aus kennt - kannst du den Objekten deinen Knotenpunkt zeigen. Sie registrieren sich dann da von sich aus. Das geht dann in Richtung Event-System.

Du definierst irgendwie ein Event ("irgendwie", weil's da mehrere brauchbare Varianten gibt) und sagst deinen Objekten, wo dieses Event ist und dass sie sich dort bitte registrieren sollen. Wenn das Event ausgelöst wird, dann meldet es sich bei allen registrierten Objekten.

Eine recht simple Variante, die aber völlig in Ordnung ist, würde so aussehen:

public static class GameOver
{
  public static event Action onGameOver;
  
  public static void Invoke()
  {
    onGameOver?.Invoke();
  }
}

Das wäre jetzt eine einzelne Klasse für ein einzelnes Event. Hab das nicht getestet, ich hoffe, ich hab da nicht irgendwo Quatsch drin ;)

Benutzt man dann so... Reaktion hinzufügen:

private void OnEnable()
{
  GameOver.onGameOver += ResetPosition;
  
  // position speichern
}

private void OnDisable()
{
  GameOver.onGameOver -= ResetPosition;
}

private void ResetPosition()
{
  // position zurücksetzen
}

Und aufrufen einfach von überall aus mit

GameOver.Invoke();

Wenn du jetzt ein neues Objekt mit Reset-Script ausstattest, musst du nichts weiter tun.

Ein Problem bei dieser Implementation ist, dass das Reset-Script jetzt nur genau auf das GameOver-Event reagieren kann, weil's ja hardcoded im Script steht. Wenn man sich da ein bisschen weiter reinfuchst, kann man ein ganzes Event-System bauen (oder sich besorgen), das beliebig viele Events verwalten kann. Für ein kleineres Projekt ist das hier allerdings völlig in Ordnung.

Und von Tags empfehle ich wie immer, die Finger zu lassen ;)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hmmm... Ich habe momentan alles in dieser einen Szene. Sprich, wenn ich sie neu laden lasse, startet das ganze mit dem Hauptmenü. Sprich, die Customer-Journey (sofern ich das jetzt einfach mal auf ein Videoadaptieren darf) wäre etwas unglücklich: "Game over - Neustart - Hauptmenü - Spiel starten". Daher wollte ich eigentlich direkt in`s Spiel starten. Sprich, alle Objekte und Eingaben auf Werkseinstellung und von neuem starten. 

Deinen Ansatz mit dem umgedrehten Spieß überfordert mich beim ersten Durchlesen grad ein wenig :D Liegt vielleicht aber auch daran, dass ich gerade mal eine Tasse Kaffee intus habe und noch nicht ganz auf Level bin. Du meinst also, grob gesprochen, im Menü-Script steuere ich das Klicken auf den Reload-Button: Nur das Klicken, erstmal ohne Funktion. Und auf jeweils auf den Objekten liegt das Reload-Script, was nach diesem Event des Button-Klickens sucht und sich dann entsprechend auslöst?

Sobald ich wieder am Projekt sitze, schaue ich mir das genauer an und versuche das einzubauen. Für den Moment danke dir aber schon mal sehr für die Idee :) 

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 3 Stunden schrieb Der_Stefan_:

Hmmm... Ich habe momentan alles in dieser einen Szene.

Joa, ist nur eine mögliche Variante. Szenenwechsel heißt halt: Alle Objekte weg, neue Objekte rein, und in vielen Situation ist das ein einfacherer Weg, als manuell viel zu löschen und zu spawnen. Aber... ist immer nur eine Möglichkeit, die man nicht nutzen muss.

vor 3 Stunden schrieb Der_Stefan_:

Deinen Ansatz mit dem umgedrehten Spieß überfordert mich beim ersten Durchlesen grad ein wenig :D Liegt vielleicht aber auch daran, dass ich gerade mal eine Tasse Kaffee intus habe und noch nicht ganz auf Level bin. Du meinst also, grob gesprochen, im Menü-Script steuere ich das Klicken auf den Reload-Button: Nur das Klicken, erstmal ohne Funktion. Und auf jeweils auf den Objekten liegt das Reload-Script, was nach diesem Event des Button-Klickens sucht und sich dann entsprechend auslöst?

Ach so... den Button habe ich noch nicht drin. GameOver.Invoke kann jeder aufrufen, der will. Wobei UnityEvents (die Boxen, wo man bei einem Button einträgt, was beim Klick passieren soll) keine statischen Methoden unterstützen.

"Suchen" tut hier aber niemand etwas. "Suchen" würde ja heißen, dass man sich irgendwo durchwühlt, bis man etwas gefunden hat. GameOver.onGameOver ist ein statisches Event und damit ist ganz eindeutig definiert, wo das Ding ist. Ist quasi wie das Einwohnermeldeamt. Du weißt, wo das Ding ist, bevor du deine Haustür verlässt, und gehst nicht so lange Straßen ab, bis du es gefunden hast. In OnEnable registriert sich jedes Reset-Objekt und in OnDisable trägt es sich wieder aus. Und wenn man das Event mit Invoke() aufruft, dann wird eben alles aufgerufen, was aktuell registriert ist.

Um bei der Metapher zu bleiben: Jetzt forderst du von jedem Einwohner, sich beim Amt anzumelden. Und wenn dann alle Einwohner Post kriegen sollen, geht der Bürgermeister zum Amt und gibt da einen Brief in Auftrag. Das wäre so im Vergleich zum aktuellen Ansatz, wo der Bürgermeister sich mühsam die Adressen aller Anwohner selber besorgt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 2 weeks later...

Ich hab am Wochenende jetzt endlich mal wieder ein bisschen weitergemacht. Sascha`s Tipp mit dem Invoke hat durchaus geholfen, ich hab auf jeden Fall erreicht was ich wollte. Mit dem Ergebnis war ich dennoch nicht zufrieden. Ich habe daher jetzt doch noch mal komplett umgebaut, das Hauptmenü in eine eigene Szene verfrachtet und hierfür eine komplett eigene Szenerie geschaffen. Das Raumschiff steht startbereit im Hangar. Eine Sirene dreht sich, die Skybox und das Licht dreht läuft am Fenster vorbei, Dampf kräuselt aus dem laufenden Motor des Raumschiffes... Ist nett geworden, ich bin zufrieden :)  Und nach Spielende, für den Neustart bin ich dann über "Load Scene" gegangen. Im Nachhinein, trotz dass ich erst noch einen Hangar bauen musste, die einfachere und vor allem jetzt auch hübschere Lösung :)

 

 

Snip1.JPG

Snip2.JPG

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...