【问题标题】:Losing reference to GameViewController from inside SpriteKit when moving between scenes - want to move between SKScene and UITableView在场景之间移动时从 SpriteKit 内部丢失对 GameViewController 的引用 - 想要在 SKScene 和 UITableView 之间移动
【发布时间】:2019-12-23 12:15:50
【问题描述】:

我有一个简单的(多场景)spritekit 游戏,想使用 UITableViewContoller 实现和显示设置屏幕。我很接近,但是当我改变场景时,我一直失去对游戏视图控制器的引用,这意味着我只能在我的游戏开始时在第一个场景中继续使用 UITableViewController。我希望有人能帮忙。我在情节提要中设置了 UITableView,并从那里的 GameViewController 设置了对 tableview 的 segue。

我的 GameViewController 文件中有以下设置:

override func viewDidLoad() {
    super.viewDidLoad()

    if let view = self.view as! SKView? {
        // Load the SKScene
        if let scene = SKScene(fileNamed: "MenuScene") {
            // Set the scale mode to scale to fit the window
            scene.scaleMode = .aspectFill

            //I'VE ADDED THIS
            if let gameScene = scene as? MenuScene {
                gameScene.viewController = self
            }
            // Present the scene
            view.presentScene(scene)
        }

        view.ignoresSiblingOrder = true
    }
}

func showSettings() {
    performSegue(withIdentifier: "openSettings", sender: self)
}

这让我从我的 SKScene 中引用了 GameViewController。然后,当用户触摸 SKLabelNode 时,我在 spritekit 中从我的 SKScene 加载 UITableViewController。我的 MenuScene 代码的更简单版本如下所示:

class MenuScene: SKScene {
var viewController: GameViewController?

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
    let touch:UITouch = touches.first!
    let positionInScene = touch.location(in: self)
    let touchedNode = self.atPoint(positionInScene)

    if let name = touchedNode.name
    {
        let transition:SKTransition = SKTransition.fade(withDuration: 0.75)
        var scene:SKScene = SKScene()

        if (name == "settingsBTN"){
            viewController?.showSettings()
        }
        else if (name == "randomBTN"){
            scene = StarScene(size: self.size)
            scene.backgroundColor = SKColor.black
            self.view?.presentScene(scene, transition: transition)
        }
    }
}

}//菜单场景结束

当我第一次开始游戏时点击设置 BTN 时,表格视图呈现良好。但是,如果我在我的游戏中访问另一个场景(例如,通过按 randomBTN)并返回菜单场景,我将无法再转到设置视图,因为我失去了对视图控制器的引用(它显示为 nil)所以我可以'不要在执行 segue 的 GameViewController 中调用 showSettings 方法(大概是因为我在视图控制器本身中设置了对视图控制器的引用)。

我不知道如何维护对我的 GameViewController 的引用,因此我可以随时显示设置屏幕。请有人帮忙。

【问题讨论】:

    标签: swift uiviewcontroller sprite-kit


    【解决方案1】:

    有两种简单的方法可以从其他类调用GameViewController 函数:通过delegate 或通过创建类实例static

    // DELEGATE
    //
    // GameViewController.swift
    
    protocol GameView_Delegate
    {
        func show_ui()
    }
    
    class GameViewController: UIViewController, GameView_Delegate
    {
        override func viewDidLoad()
        {
    
            //   ... 
    
            if let view = self.view as! SKView?
            {
                if let scene = SKScene(fileNamed: "MenuScene")
                {        
                    scene.scaleMode = .aspectFill
    
                    // pass GameViewController reference
                    scene.gv_delegate = self
                }
    
                view.presentScene(scene)
             }
    
            // ...
    
        }
    
        func show_ui()
        {
            // show ui
            // ...
        }
    }
    
    // MenuScene.swift
    
    class MenuScene: SKScene
    {
        var gv_delegate : GameView_Delegate!
    
        func settings()
        {
            // call function show_ui from GameViewController
            gv_delegate.show_ui()
        }
    
        func game_scene()
        {
            let transition = SKTransition.fade(with: UIColor.white, duration: 1.0)
            let next_scene = GameScene()
            next_scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
            next_scene.scaleMode = self.scaleMode
            next_scene.size = self.size
    
            // before moving to next scene, we must pass the delegate, so we
            // don't  lose the connection with the GameViewController
            next_scene.gv_delegate = gv_delegate
            self.view?.presentScene(next_scene, transition: transition)
        }
    }
    
    // GameScene.swift
    
    class GameScene: SKScene
    {
        var gv_delegate : GameView_Delegate!
    
        func settings()
        {
            // call function show_ui from GameViewController
            gv_delegate.show_ui()
        }
    
        func main_scene()
        {
            let transition = SKTransition.fade(with: UIColor.white, duration: 1.0)
            let next_scene = MainScene()
            next_scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
            next_scene.scaleMode = self.scaleMode
            next_scene.size = self.size
    
            // before moving to next scene, we must pass the delegate, so we
            // don't lose the connection with the GameViewController
            next_scene.gv_delegate = gv_delegate
            self.view?.presentScene(next_scene, transition: transition)
        }
    }
    

    当你进入一个新场景时,例如GameScene,在我们的例子中旧场景MainScene被释放(所有变量被清空,所有引用被销毁)并且一个新的GameScene实例被创建并显示在屏幕上。如果之后您想退出该场景并输入MainScene,则会创建该类的新实例,而GameViewController 引用为空。我们应该再次引用它或在退出场景之前传递引用(如我的示例中所示)。

    // STATIC
    //
    // GameViewController.swift
    
    class GameViewController: UIViewController
    {
        static var shared : GameViewController!
    
        override func viewDidLoad()
        {
            super.viewDidLoad()
    
            GameViewController.shared = self
    
            // ...
    
        }
    
        func show_ui()
        {
            // show ui
            // ...
        }
    }
    
    
    // MenuScene.swift
    
    class MenuScene: SKScene
    {
        func settings()
        {
            // call function show_ui from GameViewController
    
            GameViewController.shared.show_ui()
        }
    }
    

    阅读每种方法的优缺点,然后选择最适合您的应用的方法。另外,避免在 SpriteKit 中使用 UIKit。

    【讨论】:

    • 感谢您提供非常全面的答案。我实施了第二种“静态”方法,这似乎对我很有效。我会有很多场景,所以认为在场景之间传递参考可能不是最好的方法。当您说避免在 spritekit 中使用 UIKit 时,您的意思是避免采用这些方法中的任何一种吗?还是可以使用我正在使用的方法。对我来说,在用户习惯的设置屏幕上使用一致的 UI 控件是有意义的
    • 这些方法是为 Spritekit 游戏设计的。根据情况,我在我的游戏中同时使用它们。根据您的情况,我还建议使用静态方法。关于 UIKit,你根本不应该使用它。您应该创建一个 SettingsScene,用户可以从 GameOverScene 或 MainScene 访问它,因为您不想将游戏 GUI 与 UIKit 标准 GUI 结合起来。您应该在所有应用程序中使用相同的设计。
    猜你喜欢
    • 1970-01-01
    • 2023-03-15
    • 2020-12-07
    • 2012-02-21
    • 1970-01-01
    • 2016-02-11
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    相关资源
    最近更新 更多