You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中使用SpriteView实现SpriteKit场景切换的问题

解决SpriteKit场景切换后后台返回丢失的问题

问题根源在于:你最初的实现中,SpriteView仅持有TestGameScene的初始引用,当通过SKView.presentScene切换到新场景后,SwiftUI并未跟踪这个场景变化。应用进入后台再返回时,SpriteView会重新初始化并使用初始的TestGameScene,导致已切换的场景被销毁。

以下是保留SpriteKit过渡效果的最佳解决方案:

方案1:用@State管理当前场景引用

通过SwiftUI的状态变量持有当前活跃场景的引用,确保后台返回时不会丢失场景实例,同时保留SpriteKit的过渡动画。

1. 修改ContentView,添加状态管理

struct ContentView: View {
    // 用@State存储当前展示的场景
    @State private var currentScene: SKScene = TestGameScene()
    
    var body: some View {
        SpriteView(scene: currentScene)
            .onAppear {
                currentScene.scaleMode = .resizeFill
            }
    }
}

2. 给SKScene添加切换回调

在场景类中添加闭包,用于通知SwiftUI更新当前场景引用,同时保留SpriteKit的过渡调用:

class TestGameScene: SKScene {
    // 定义切换回调,通知SwiftUI更新场景
    var onSceneSwitch: ((SKScene) -> Void)?
    
    override func didMove(to view: SKView) {
        size = view.frame.size
        scaleMode = .resizeFill
        let textNode = SKLabelNode(text: "Game Scene")
        textNode.position = CGPoint(x: size.width/2, y: size.height/2)
        addChild(textNode)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let gameOverScene = TestGameOverScene()
        // 给新场景设置回调(支持从GameOver切回)
        gameOverScene.onSceneSwitch = { [weak self] newScene in
            self?.view?.presentScene(newScene, transition: .doorsOpenVertical(withDuration: 0.5))
            self?.onSceneSwitch?(newScene)
        }
        // 使用SpriteKit过渡展示新场景
        view?.presentScene(gameOverScene, transition: .fade(withDuration: 1.0))
        // 通知SwiftUI更新当前场景引用
        onSceneSwitch?(gameOverScene)
    }
}
class TestGameOverScene: SKScene {
    var onSceneSwitch: ((SKScene) -> Void)?
    
    override func didMove(to view: SKView) {
        size = view.frame.size
        scaleMode = .resizeFill
        backgroundColor = .red
        let textNode = SKLabelNode(text: "Game Over")
        textNode.position = CGPoint(x: size.width/2, y: size.height/2)
        addChild(textNode)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let gameScene = TestGameScene()
        gameScene.onSceneSwitch = { [weak self] newScene in
            self?.view?.presentScene(newScene, transition: .doorsOpenVertical(withDuration: 0.5))
            self?.onSceneSwitch?(newScene)
        }
        view?.presentScene(gameScene, transition: .fade(withDuration: 1.0))
        onSceneSwitch?(gameScene)
    }
}

方案2:用ObservableObject管理多场景(适合复杂游戏)

如果游戏场景较多,可单独用一个管理器类统一管理场景状态:

class SceneManager: ObservableObject {
    @Published var currentScene: SKScene
    
    init() {
        let initialScene = TestGameScene()
        initialScene.onSceneSwitch = { [weak self] newScene in
            self?.currentScene = newScene
        }
        self.currentScene = initialScene
    }
}

struct ContentView: View {
    @StateObject private var sceneManager = SceneManager()
    
    var body: some View {
        SpriteView(scene: sceneManager.currentScene)
            .onAppear {
                sceneManager.currentScene.scaleMode = .resizeFill
            }
    }
}

核心要点

  • 必须让SwiftUI持有当前场景的强引用,避免后台时场景被系统释放
  • 通过闭包回调让SpriteKit场景通知SwiftUI更新状态,同时保留原生过渡动画
  • 不要直接替换SpriteView的scene参数跳过SKView.presentScene,否则会丢失SpriteKit的过渡效果

内容的提问来源于stack exchange,提问作者rayaantaneja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 01:00:25