Change SKScene using presentScene()

烈酒焚心 提交于 2020-01-14 02:48:09

问题


In my SpriteKit Game i'm using:

    self.scene!.removeFromParent()
    let skView = self.view! as SKView
    skView.ignoresSiblingOrder = true
    var scene: PlayScene!
    scene = PlayScene(size: skView.bounds.size)
    scene.scaleMode = .AspectFill
    skView.presentScene(scene, transition: SKTransition.fadeWithColor(SKColor(red: 25.0/255.0, green: 55.0/255.0, blue: 12.0/255.0, alpha: 1), duration: 1.0))

to move from one scene to another. But how can I go back to the original scene? Using the same principle of code always led to a major crash..


回答1:


I made an example where global structure is used to track the info about previousScene. It can be done with a custom property as well, or by using userData which every node has. The logic is the same. Also, I've removed debugging code (debug label code etc.) because it is not important for everything to work.

Example might be better if I added a few buttons where each links to the certain scene, but I left just one button to keep everything short as possible.

What you need to know about this example (you will change this rules according to your game, but the logic is the same - set the previousScene before an actual transition):

  • there are three scenes, WelcomeScene (default one), MenuScene and a GameScene.
  • tapping on the black button takes you to the GameScene. There is an exception to this rule when current scene is a GameScene. In that case, transition will take you to the previousScene.
  • tapping anywhere around the black button will take you to the previous scene. There is an exception to this rule when WelcomeScene is loaded for the first time (previousScene is not set) and a transition will take you to the MenuScene in that case.

-in your GameViewController you should set up a WelcomeScene to be a default one. Otherwise, you should change a code a bit to handle situations what happening when previousScene is not set (like I did in touchesBegan of WelcomeScene).

So those are rules I've made, just in order to make all those transitions a bit more meaningful...

Here is the code (BaseScene.swift):

import SpriteKit

enum SceneType: Int {

    case WelcomeScene   = 0
    case MenuScene      //1
    case GameScene      //2  
}

struct GlobalData
{
    static var previousScene:SceneType?
    //Other global data...
}

class BaseScene:SKScene {

    let button = SKSpriteNode(color: SKColor.blackColor(), size: CGSize(width: 50, height: 50))

    override func didMoveToView(view: SKView) {
        setupButton()
    }

    private func setupButton(){

        if (button.parent  == nil){

            //Just setup button properties like position, zPosition and name

            button.name = "goToGameScene"
            button.zPosition = 1
            button.position = CGPoint(x: CGRectGetMidX(frame), y: 100)
            addChild(button)
        }
    }

    func goToScene(newScene: SceneType){

        var sceneToLoad:SKScene?

        switch newScene {

        case SceneType.GameScene:

            sceneToLoad = GameScene(fileNamed: "GameScene")

        case SceneType.MenuScene:

            sceneToLoad = MenuScene(fileNamed: "MenuScene")

        case SceneType.WelcomeScene:

            sceneToLoad = WelcomeScene(fileNamed:"WelcomeScene")

        }

        if let scene = sceneToLoad {

            scene.size = size
            scene.scaleMode = scaleMode
            let transition = SKTransition.fadeWithDuration(3)
            self.view?.presentScene(scene, transition: transition)
        }
    }
}

Every scene (WelcomeScene, MenuScene, GameScene) inherits from a BaseScene class (which is subclass of a SKScene). I guess, there is no need to explain that, but feel free to ask if something confuses you. The important method here (which is used by every subclass) is goToScene(scene:SceneType) and its parameter (of type SceneType) which tells us what type of scene a method should load.

SceneType is just an enum which holds integers...So actually we are not working with objects here, thus there is no fear of strong reference cycles.

Next, there are other scenes (WelcomeScene.swift):

import SpriteKit


class WelcomeScene:BaseScene {


    override func didMoveToView(view: SKView) {

        super.didMoveToView(view)

        self.backgroundColor = SKColor.darkGrayColor() 
    }

    deinit {print ("WelcomeScene deinited")}

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

        let touch = touches.first

        if let location = touch?.locationInNode(self){

            //Give a priority to a button - if button is tapped go to GameScene
            let node = nodeAtPoint(location)
            if node.name == "goToGameScene"{
                GlobalData.previousScene = SceneType.MenuScene
                goToScene(SceneType.GameScene)
            }else{
                //Otherwise, do a transition to the previous scene

                //Get the previous scene
                if let previousScene = GlobalData.previousScene {

                    GlobalData.previousScene = SceneType.WelcomeScene
                    goToScene(previousScene)

                }else{

                    // There is no previousScene set yet? Go to MenuScene then...
                    GlobalData.previousScene = SceneType.WelcomeScene
                    goToScene(SceneType.MenuScene)

                }
            }       
        }      
    }
}

To keep short as possible, everything is commented. Next code (MenuScene.swift):

import SpriteKit

class MenuScene: BaseScene  {

    override func didMoveToView(view: SKView) {

        super.didMoveToView(view)
        backgroundColor = SKColor.purpleColor()
    }

    deinit {
        print ("MenuScene deinited") //If this method isn't called, you might have problems with strong reference cycles.
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

        let touch = touches.first

        if let location = touch?.locationInNode(self){

            //Give a priority to a button - if button is tapped go to GameScene
            let node = nodeAtPoint(location)
            if node.name == "goToGameScene"{
                GlobalData.previousScene = SceneType.MenuScene
                goToScene(SceneType.GameScene)
            }else{
                //Otherwise, do a transition to the previous scene

                //Get the previous scene
                if let previousScene = GlobalData.previousScene {

                    GlobalData.previousScene = SceneType.MenuScene
                    goToScene(previousScene)

                } 
            }
        } 
    }
}

And for the end (GameScene.swift):

import SpriteKit

class GameScene: BaseScene{

    override func didMoveToView(view: SKView) {

        super.didMoveToView(view)

     self.backgroundColor = SKColor.orangeColor() 
    }

    deinit {print ("GameScene deinited")}

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

        //Here, we ignore black button because we don't want to transition to the same scene

         if let previousScene = GlobalData.previousScene {

            GlobalData.previousScene = SceneType.GameScene
            goToScene(previousScene)  
        }
    }
}

Preview:

Just read again the rules from the beginning and you will be fine (eg. in GameScene black button doesn't work, or on first launch previousScene is not set , so you will be transitioned to the MenuScene by default).

That would be it. Hope this helps a bit. You can copy and paste the code to test it and improve it to your needs. Still, not sure that you really need this. It looks that you just need to correctly transition between scenes.

HINT: What is important here is that every scene BaseScene, WelcomeScene... has it own .sks file. You create those from File->New->File->Resource and name it appropriately (like BaseClass.sks, WelcomeScene.sks...) Also, it is your job to maintain the state of GlobalData.previousScene variable (eg. set it before the transition is made).




回答2:


You would need to create a property in your new scene that stores the previous one, something like previousScene. Then you can set it like this: scene.previousScene = self.scene. In you new scene, you can now go back to the previous scene with skView.presentScene(previousScene)

And I'd advise against naming the new scene you are going to present scene because your current scene is also named scene, so if you accidentally forget the self in self.scene then that may cause a lot of confusion. I'd name it something like newScene or sceneToPresent.

Also, your first line, self.scene!.removeFromParent(), isn't necessary. You don't need to remove the current scene before presenting a new one.



来源:https://stackoverflow.com/questions/34498982/change-skscene-using-presentscene

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!