问题
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