ios - 使用 SpriteView(isPaused:) 从 SwiftUI 暂停 SpriteKit 场景,而不是每次都重新初始化它?
问题描述
我正在尝试使用SpriteView初始化程序中的isPaused:
参数来暂停SwiftUI 中的状态属性更改时。SKScene
isPaused:
但是当我在初始化程序中使用状态变量作为参数时,例如:
SpriteView(scene: scene, isPaused: showingLevelChooser)
而不仅仅是:
SpriteView(scene: scene)
每次变量更改时都会重新创建场景,这不是我想要的。我只想暂停游戏。
我很困惑这个isPaused:
论点应该如何工作。SpriteView 文档中没有太多信息。
据我了解,这是因为 SwiftUI 重新创建了依赖于状态的视图。但是如果是这样的话,你怎么能从 SwiftUI 中暂停 SpriteKit 场景,而不是每次都重新初始化呢?
我在这里创建了一个示例 Xcode 13 项目:https ://github.com/clns/SpriteView-isPaused
正在屏幕SKScene
上显示“经过的时间” ,以秒为单位。每次呈现 SwiftUI 工作表并且状态变量showingLevelChooser
发生变化时,计时器都会从 0(零)开始,因为场景会重新初始化。
所有相关代码都在ContentView.swift中。
class GameScene: SKScene {
private let label = SKLabelNode(text: "Time Elapsed:\n0")
private var lastUpdateTime : TimeInterval = 0
override func didMove(to view: SKView) {
addChild(label)
}
override func update(_ currentTime: TimeInterval) {
if (self.lastUpdateTime == 0) {
self.lastUpdateTime = currentTime
}
let seconds = Int(currentTime - lastUpdateTime)
label.text = "Time Elapsed:\n\(seconds)"
label.numberOfLines = 2
}
}
struct ContentView: View {
@State private var showingLevelChooser = false
var scene: SKScene {
let scene = GameScene()
scene.size = CGSize(width: 300, height: 400)
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
scene.scaleMode = .fill
return scene
}
var body: some View {
ZStack {
SpriteView(scene: scene, isPaused: showingLevelChooser)
.ignoresSafeArea()
VStack {
Button("Level Chooser") {
showingLevelChooser.toggle()
}
Spacer()
}
}
.sheet(isPresented: $showingLevelChooser) {
VStack {
Button("Cancel") {
showingLevelChooser.toggle()
}
Text("Level Chooser")
}
}
}
}
解决方案
这里有两个问题需要注意。第一个是你想有一个参考你SKScene
会坚持下去。将其作为计算属性View
将不起作用(正如您所经历的那样),因为它View
是可传递的,并且每次重新加载时,您都会得到一个新的SKScene
.
对此有多种可接受的解决方案,但我选择将其封装SKScene
在一个ObservableObject
您可以引用的 viaStateObject
中。您甚至可以尝试制作SKScene
anObservableObject
本身(此处未显示)。
其次,您在屏幕上确定/显示“暂停”的逻辑将很难准确地看到,因为它总是只显示经过的时间——在暂停期间没有不计算时间的逻辑。我用一个显示更新次数的简单计数器替换了它。这样,您可以清楚地看出场景确实已经暂停(并且没有更新)。
class SceneStore : ObservableObject {
var scene = GameScene()
init() {
scene.size = CGSize(width: 300, height: 400)
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
scene.scaleMode = .fill
}
}
class GameScene: SKScene {
private let label = SKLabelNode(text: "Updates: 0")
private var updates = 0
override func didMove(to view: SKView) {
addChild(label)
}
override func update(_ currentTime: TimeInterval) {
updates += 1
label.text = "Updates: \(updates)"
label.numberOfLines = 2
}
}
struct ContentView: View {
@State private var showingLevelChooser = false
@StateObject private var sceneStore = SceneStore()
var body: some View {
ZStack {
SpriteView(scene: sceneStore.scene, isPaused: showingLevelChooser)
.ignoresSafeArea()
VStack {
Button("Level Chooser") {
showingLevelChooser.toggle()
}
Spacer()
}
}
.sheet(isPresented: $showingLevelChooser) {
VStack {
Button("Cancel") {
showingLevelChooser.toggle()
}
Text("Level Chooser")
}
}
}
}
推荐阅读
- plsql - pl/sql: 你的 SQL 语法有错误如何修复这个错误?
- arrays - 使用 arrayLength 在 typescript 中创建 2D 空数组
- java - 如何计算 8.0 设备上的屏幕解锁尝试次数?
- flutter - 如果有很多要滑动的项目,滑块会挂起
- c++ - 如何杀死被条件变量锁定的线程?
- c# - 获取 Azure MVC Web 应用程序的应用程序 URL 以与 webhook 一起使用
- angular - ng-circle-progress 中的可变百分比
- ios - 在 iOS 13 中使用 UIImagePickerController 时崩溃
- c++ - 在 OpenMP 中读取/写入共享向量会减慢程序速度
- python-3.x - python - 如何使用python中后面的单词检索字符串中的单词?