ios - 使用 SpriteView 从 SwiftUI 暂停 SpriteKit 场景(isPaused :), 而无需每次都重新初始化?

标签 ios swift swiftui sprite-kit

我正在尝试使用 SpriteView 中的 isPaused: 参数当 SwiftUI 中的状态属性发生更改时,初始化程序会暂停 SKScene

但是当我在初始化程序中使用状态变量作为 isPaused: 的参数时,例如:

SpriteView(scene: scene, isPaused: showingLevelChooser)

而不是仅仅:

SpriteView(scene: scene)

每次变量更改时都会重新创建场景,这不是我想要的。我只想暂停游戏。

我很困惑 isPaused: 参数应该如何工作。 SpriteView documentation中没有太多信息。 .

据我了解,发生这种情况是因为 SwiftUI 重新创建了依赖于状态的 View 。但如果是这种情况,如何从 SwiftUI 暂停 SpriteKit 场景,而不需要每次都重新初始化它?

我在这里创建了一个示例 Xcode 13 项目:https://github.com/clns/SpriteView-isPaused

SKScene 正在屏幕上显示“已用时间”(以秒为单位)。每次呈现 SwiftUI 工作表并且状态变量 showingLevelChooser 发生变化时,计时器都会从 0(零)开始,因为场景会重新初始化。

Visual_Demonstration

所有相关代码都在 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 中,您可以通过 StateObject 保存对它的引用>。您甚至可以尝试将 SKScene 设为 ObservableObject 本身(此处未显示)。

其次,你在屏幕上确定/显示“暂停”的逻辑有点难以准确地看到,因为它总是只显示耗时——不涉及不计算暂停期间时间的逻辑。暂停。我用一个显示更新次数的简单计数器替换了它。这样,您可以清楚地看出场景确实已暂停(并且不更新)。


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

关于ios - 使用 SpriteView 从 SwiftUI 暂停 SpriteKit 场景(isPaused :), 而无需每次都重新初始化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69442079/

相关文章:

Swift 子类通过绑定(bind)到父类(super class)来采用自定义协议(protocol)

appdelegate - 如何使用@EnvironmentObject 在 SwiftUI 中的 AppDelegate 和 SceneDelegate/Views 之间共享数据

ios - 如何在 SwiftUI 中对列表中的值求和?

ios - 对 iOS 5 NIB 使用 iOS 6 字体

ios - 将字符串操作为 AttributedString 后调整元素 - Swift 4

ios - Swift 无法绕过我的 UIButton 的角

swift - “如果没有更多上下文,表达类型就不明确”

iphone - numberofRowsinSection 可以返回 NSMutableArray 的值吗?

ios - 不在事件 View Controller 上时如何执行 segue "behind the scenes"?

Swift//NSPopUpButton.addingItems() 问题