场景之间的 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)")
}
}
我如何在不重置游戏的情况下在场景之间转换,因为 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)")
}
}