如何在SwiftUI中使用SpriteView在场景之间进行转换?

问题描述 投票:0回答:1

我想在两个场景之间切换:

class TestGameScene: SKScene {
    override func didMove(to view: SKView) {
        size = view.frame.size
        scaleMode = .resizeFill
        let textNode = SKLabelNode(text: "Game Scene")
        textNode.position.x = size.width / 2
        textNode.position.y = size.height / 2
        addChild(textNode)
    }
}

class TestGameOverScene: SKScene {
    override func didMove(to view: SKView) {
        size = view.frame.size
        scaleMode = .resizeFill
        backgroundColor = .red
        let textNode = SKLabelNode(text: "Game Over")
        textNode.position.x = size.width / 2
        textNode.position.y = size.height / 2
        addChild(textNode)
    }
}

我目前正在通过

TestGameScene
展示
SpriteView
:

struct ContentView: View {
    var body: some View {
        SpriteView(scene: TestGameScene())
    }
}

为了过渡到 Game Over 场景,我覆盖了

touchesBegan(_:with:)
中的
TestGameScene
以呈现 Game Over 场景,如下所示:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    view!.presentScene(TestGameOverScene(), transition: .fade(withDuration: 1.0))
}

虽然这有效,但如果应用程序进入后台,

TestGameOverScene
就会被取消初始化,从而导致游戏结束场景消失。使用 SpriteView 时有没有办法防止这种情况发生,或者有更好的方法在两个场景之间进行转换?我想使用 SpriteKit 提供的精美过渡,例如。
.doorsOpenVertical(withDuration: 0.5)
等等等等

swift swiftui transition skscene spriteview
1个回答
0
投票

在尝试了一些想法之后,我想出了可行的解决方案,但是,我希望对这种方法的好坏进行一些批评/分析,因为我对内存管理不太了解。

此类为

ContentView
提供了使用
SpriteView
渲染的场景。这是一个已发布的属性,当它发生更改时,应该通知 SwiftUI。

final class SceneProvider: ObservableObject { 
    @Published var scene: SKScene

    init() { 
        let initialScene = TestGameScene()
        self.scene = initialScene
        initialScene.viewState = self
    }
}

添加了对 SwiftUI 呈现的场景的绑定/引用。我们将更新此引用到新呈现的场景以通知 SwiftUI。

class TestGameScene: SKScene { 
    unowned var viewState: SceneProvider!
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let sceneToPresent = TestGameOverScene()
        viewState.scene = sceneToPresent  // << we do this so SwiftUI updates
        view!.presentScene(sceneToPresent, transition: .fade(withDuration: 1.0))
    }
    // .. Rest is the same
}

从这里,我也许可以创建一个自定义的

SKScene
子类来封装所有这些行为。

当然,

struct ContentView: View { 
    @StateObject var sceneProvider = SceneProvider()  // << @StateObject ensures the container for the presented scene, ie. SceneProvider, isn't deallocated
    
    var body: some View { 
        SpriteView(scene: sceneProvider.scene)
    }
}

使用这种方法,当

TestGameScene
出现时,
TestGameOverScene
会被取消初始化 - 这很好(我认为)。除此之外,即使您转到后台并返回,SwiftUI 也会维护新呈现的场景,因为
@Published
属性和
@StateObject
使 SwiftUI 保持最新状态。

由于

SceneProvider
仅保留一个场景,因此我们不会有多个场景只占用内存空间 - 这又很好(我知道这肯定很好)。

希望得到一些对此的反馈。

© www.soinside.com 2019 - 2024. All rights reserved.