2016-08-02 11 views
2

Wie kann ich zwischen Szenen ohne mein Spiel zurücksetzen, weil didMoveToView() aufgerufen wurde und alle meine Instanz Variablen neu initialisiert. Zum Beispiel habe ich eine Spielszene und eine Ladenszene. Wenn ich von meiner Ladenszene zu meinem Spiel übergehe, wird das Spiel zurückgesetzt. Gibt es eine Möglichkeit, dies zu verhindern, oder wie behalte ich den gleichen Zustand meines Spiels beim Übergang zwischen den Szenen?SpriteKit Übergang zwischen Szenen ohne das Zurücksetzen des Spiels

Antwort

1

Sie haben viele Optionen, um den dauerhaften Status Ihrer Spielszenen zu erhalten. Ich habe zwei Ansätze aufgelistet, die ich verwendet habe.

Option A: Halten Sie einen Verweis auf die Szene

Wenn eine Szene für einen neuen ausgetauscht wird, wird die Szene oft vollständig aus dem Speicher entfernt. Wenn Sie eine Referenz für das Szenenobjekt irgendwo anders halten und diese Referenz präsentieren, gehen keine Daten verloren.

Um eine Referenz über die Zeit (und präsentiert die Szene wieder, wenn erforderlich) zu halten, habe ich eine Szene Moderator Klasse mit einer statischen Instanz wie die folgenden empfehlen:

class SceneCoordinator { 
    static var shared = SceneCoordinator() 

    var gameScene : GameScene? 
    var shopScene : ShopScene? 
} 

, wenn Sie ein GameScene initialisieren, auch registrieren es mit Ihrem mit SceneCoordinator.shared.gameScene = self. Wenn Sie dann von einer anderen Szene weggehen, können Sie die in der Koordinatorklasse gespeicherte Instanz präsentieren.

didMoveToView() wird immer noch auf der Szene aufgerufen werden, wenn es erneut präsentiert wird. Sie könnten Ihren gesamten Initialisierungscode in eine separate Funktion verschieben, eine neue Instanzvariable wie var isInitialized = false erstellen und Ihren Inhalt nur dann initialisieren, wenn er falsch ist (und nach der Initialisierung auf "True" setzen).

Das Problem mit diesem Ansatz ist, dass Szenenobjekte teuer sind, was bedeutet, dass Sie einen großen Overhead aufbauen können, indem Sie keine Szenen freigeben.

Option B: Modell Struct

Der bessere Weg (die auch für eine einfachere reinit einer Szene erlauben würde, nachdem die App geschlossen) wäre ein Datenmodell des Spiels Staat zu schaffen, und eine Funktion zur Verfügung stellen um eine GameScene von Ihrem Modellobjekt zu erstellen.

Diese Methode ist konsistenter mit dem Design des Model-View-Controllers und ermöglicht eine viel leichtere Darstellung Ihrer Szenen und Daten.

wie:

struct GameModel { 
    var coins : Int 
} 

class GameScene : SKScene { 
    var state : GameModel 

    convenience init(size: CGSize, state: GameModel) { 
     self.state = state 
     // set up scene content from state 
    } 

    // lots of fun game scene logic 

    func playerCollectedCoin() { 
     state.coins += 1 
    } 

    func moveToShopScene() { 
     // init a new shop scene with the state of this scene 
     let shop = ShopScene(size: self.view!.bounds.size, state: self.state) 
     (self.view as! SKView).presentScene(scene) 
    } 
} 

class ShopScene : SKScene { 
    var state : GameModel 

    convenience init(size: CGSize, state: GameModel) { 
     self.state = state 
     // set up scene content from state 
    } 

    // lots of fun shop scene logic 

    func playerSpentCoins(amount: Int) { 
     state.coins -= amount 
    } 

    func moveToGameScene() { 
     // init a new game scene with the updated state of this scene 
     let game = GameScene(size: self.view!.size, state: self.state) 
     (self.view as! SKView).presentScene(game) 
    } 
} 
+0

Was sind diese Vorteile in diesem Beispiel? Benötigen Sie didMoveToView nicht in einer SKScene? Was ist, wenn Sie mehrere Modelle haben wollen? Wie ein Personenmodell, ein Feindmodell usw. Würde Ihre Szene einen Bezug zu jedem haben? Oder hätte es nur einen Verweis auf ein GameModel und dann hätte Ihr GameModel einen Verweis auf jedes GameModel? Ich versuche, gutes Design für meine zukünftigen Apps zu verstehen. – Brejuro

+0

didMoveToView() muss nicht in einer SKScene-Unterklasse überschrieben werden. Es wird oft außer Kraft gesetzt, um Inhalte einzurichten. Aber wieder ist es nicht erforderlich. Mein Modellbeispiel ist hier eher trivial, Ihr Model-Objekt wäre komplexer, einschließlich allem, was Sie benötigen, um eine Szene nach dem Ausrangieren neu zu initialisieren (einschließlich Listen von feindlichen Positionen usw.). Das Erstellen zusätzlicher Modellstrukturen würde helfen, die Dinge sauber zu halten. Anstatt eine Struktur zu umgehen, können Sie auch Alessandros Rat befolgen und eine gemeinsame Instanz Ihrer Modellinformationen behalten. – cpimhoff

+0

Ein Convenience-Initialisierer ermöglicht es Ihnen, einen neuen Initialisierer zu definieren, der von ihm einen Initialisierer aufruft. Hier würde also die Convenience-Initialisierung (size: model :) self.init (size :) aufrufen und dann die Modellinformationen verwenden, um den Spielinhalt einzurichten. – cpimhoff

2

Über Ihre Frage möchte ich Ihre Aufmerksamkeit auf drei Elemente konzentrieren:

  • 1) Modelle
  • 2) Geteilt Instance Manager
  • 3) Subclassing

1) In statt immer wieder in Ihrem SKScene oder SKNode die gleichen gemeinsamen Eigenschaften (struct, vars oder benutzerdefinierte Klassen ..) zu wiederholen, könnten Sie eine Klasse erstellen, die Ihre gemeinsamen Eigenschaften enthalten.

2) Es ist sehr angenehm für Ihr Projekt, einen gemeinsam genutzten Instanzmanager zu haben: zum Beispiel einen Spielmanager (gemeinsam genutzte Instanzklasse) mit Eigenschaften aller Manager (gemeinsam genutzte Instanzklassen) Ihres Projekts (ein Beispiel unten):

  • Netzwerk-Manager (überprüft jedes Mal seiner Update-Methode, wenn WIFI/3G/LTE steht zur Verfügung und speichern Werte genannt wird)
  • Audiomanager (gibt gemeinsame Eigenschaften über Untermalung, Wirkung, Volumen ...
  • Benachrichtigungsmanager (Zeige Popups jeder Art: Erfolg, Warnung, Fehler ...)
  • Dateimanager (Speichern von Dateien, klare Verz ... Datei und lesen)
  • etc ..

auch Ihr Spiel Eigenschaften Modell wie Einstellungen und Ihr Spiel Vars.

3) Wenn Sie zum Beispiel ein rootNode in Ihren Szenen benötigen (um Elemente zum Pausieren anstelle eines anderen hinzuzufügen, um ein Objekt zum selben verankerten Knoten hinzuzufügen, um einen Hintergrund zu erstellen ..) können Sie eine Unterklasse zu einem angepassten SKScene Habe diese Eigenschaften.


Einige Code zu zeigen, wie Ihr Projekt aussehen könnte, mit einem GameManager Start:

class GameManager: NSObject { 
    static let sharedInstance = GameManager() 
    var allProducts = Products() 
} 

Ihre GameScene Klasse Instanz eine einzelne Instanz GameManager wie folgt zugreifen können:

class GameScene: SKScene { 
    let gameManager = GameManager.sharedInstance 
    override func didMoveToView(view: SKView) { 
     print("the current number of shoes is \(self.gameManager.allProducts.shoes)") 
    } 
} 

Ihre ShopScene-Klasseninstanz kann auf dieselbe einzelne Instanz von GameManager wie folgt zugreifen:

class ShopScene: SKScene { 
     let gameManager = GameManager.sharedInstance 
     override func didMoveToView(view: SKView) { 
      print("the current number of shoes is \(self.gameManager.allProducts.shoes)") 
    } 
} 
+1

Ich bin nicht so gut mit Design-Mustern, es ist irgendwie schwer für mich, dieses Beispiel zu verstehen. Warum hat GameManager eine statische Instanz? Wären dann nicht alle Variablen im GameManager statisch? – Brejuro

+1

Erste Frage: Es ist die korrekte Syntax in swift 2.x, um eine gemeinsame Instanz zu implementieren (eine Instanz wird nur einmal gestartet). Zweite Frage: Nein, Sie können Ihre normalen vars deklarieren, es ist die Klasse selbst eine statische Instanz, aber seine Eigenschaften können deklariert werden, wie Sie es wünschen. –