场景之间的 SpriteKit 过渡无需重置游戏

SpriteKit transition between scenes without resetting game

我如何在不重置游戏的情况下在场景之间转换,因为 didMoveToView() 被调用并重新初始化了我的所有实例变量。例如我有一个游戏场景和一个商店场景。当我从我的商店场景过渡到我的游戏时,游戏会重置。有什么方法可以防止这种情况,或者如何在场景之间转换时保持游戏的相同状态?

您有很多选项可以在游戏场景中保持持久状态。我列出了我使用过的两种方法。

选项 A:维护对场景的引用

当场景换成新场景时,场景通常会从内存中完全删除。如果您在其他地方持有场景对象的引用,并提供该引用,则不会丢失任何数据。

为了随着时间的推移保持引用(并在需要时再次呈现场景),我推荐使用静态实例的场景呈现器 class,如下所示:

class SceneCoordinator {
    static var shared = SceneCoordinator()

    var gameScene : GameScene?
    var shopScene : ShopScene?
}

当您初始化 GameScene 时,也将其注册到 SceneCoordinator.shared.gameScene = self。然后,当从另一个场景过渡时,您可以呈现您存储在协调器中的实例 class.

didMoveToView()再次呈现时仍然会在现场调用。您可以将所有初始化代码移动到一个单独的函数,创建一个新的实例变量,例如 var isInitialized = false,并且仅在内容为 false 时初始化您的内容(并在初始化后将其设置为 true)。

这种方法的问题是场景对象很昂贵,这意味着您可能会因不允许场景被释放而增加大量开销。

选项 B:模型结构

更好的方法(这也允许在您的应用程序关闭后更容易地重新初始化场景)是创建游戏状态的数据模型,并提供从模型对象创建 GameScene 的函数。

这种方式更符合Model-View-Controller的设计模式,让你的场景和数据轻量化很多。

如:

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)
    }
}

关于你的问题,我想把你的注意力集中在三个要素上:

  • 1) 型号
  • 2) 共享实例管理器
  • 3) 子classing

1) 您可以创建一个 class 包含您的共享属性。

2) 拥有一些共享实例管理器对您的项目来说非常舒服:例如游戏管理器(共享实例class)具有所有管理器(共享实例classes)的属性您的项目(下面的示例):

  • 网络管理员(每次调用他的更新方法时检查 WIFI/3G/LTE 是否可用并存储值)
  • 音频管理器(提供有关背景音乐、效果、音量的共享属性...
  • 通知管理器(显示任何类型的弹出窗口:成功、警告、错误...)
  • 文件管理器(保存到文件并从文件中读取,清除目录...)
  • 等..

还有您的游戏属性模型,例如设置和游戏变量。

3) 例如,如果您需要在场景中使用 rootNode(添加要暂停的元素而不是其他元素,将对象添加到同一个锚定节点,制作背景......)您可以子class 到具有此属性的自定义 SKScene


一些代码显示您的项目如何出现,从 GameManager 开始:

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

您的 GameScene Class 实例可以像这样访问 GameManager 的单个实例:

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

您的 ShopScene Class 实例可以像这样访问同一个 GameManager 实例:

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