首页 > 解决方案 > 在场景之间移动时从 SpriteKit 内部丢失对 GameViewController 的引用 - 想要在 SKScene 和 UITableView 之间移动

问题描述

我有一个简单的(多场景)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 的引用,以便我可以随时显示设置屏幕。请有人帮忙。

标签: swiftuiviewcontrollersprite-kit

解决方案


有两种简单的方法可以GameViewController从其他类调用函数:通过 adelegate或通过创建类实例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。


推荐阅读