SpriteKit how to create and transition to different scenes (Swift language)
It doesn't seem like you have looked into Apple's documentation. They have an excellent guide for SpriteKit. Also, the tutorials at RayWenderlich.com within the spriteKit category are phenomenal. I haven't read much into Swift yet, but it is my understanding that Swift can be written alongside Objective-C.That being said, I'll give you a starting point (in Objective-C) .
1) Calling the initial scene:
Within your main view controller, you will want to cast the VC's view
property to an SKView
within viewDidLoad
like so:
SKView *spriteKitView = (SKView *)self.view;
some useful debugging properties on SKView
:
spriteKitView.showsFPS = YES;
spriteKitView.showsNodeCount = YES;
spriteKitView.showsPhysics = YES;
You will have to configure each scene separately (subclass SKScene
). After you have done this, import the main scene to your main VC's class implementation. To present the main scene (still in viewDidLoad
):
MyScene *mainScene = [MyScene new];
[spriteKitView presentScene: mainScene];
2) transition between scene via label (or any other SKNode
) :
Set relevant nodes as properties and present a new scene via:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
if ([self.playAgainButton containsPoint:touchLocation]) {
[self presentGamePlayScene];
} else if([self.mainMenuButton containsPoint:touchLocation]) {
[self presentMainMenu];
}
}
- (void)presentGamePlayScene {
gamePlayScene *newGamePlay = [[gamePlayScene alloc]init];
newGamePlay.scaleMode = SKSceneScaleModeAspectFill;
SKTransition *arbitraryTransition = [SKTransition crossFadeWithDuration:2.0]; //look into SKTransition for a plethora of different options.
[self.view presentScene:newGamePlay transition:arbitraryTransition];
}
3) To change scenes via contact, conform to the protocol SKPhysicsContactDelegate
within any given SKScene
. Within that scene's implementation:
- (void)didBeginContact:(SKPhysicsContact *)contact {
if(contact.bodyA.categoryBitMask == enemyCategory && contact.bodyB.categoryBitMask == arbitraryNodeCategory) {
[self presentGameOverScene];
}
}
An SKPhysicsContact
has 2 colliding SKPhysicsBody
s attached to it via properties. These colliding nodes must have their SKPhysicsBody
's initialized. In addition, they must have their contactBitMask
property set on their SKPhysicsBody
like so:
@implementation SomeNode
- (instanceType)initCollidingNode {
if(self = [super init]) {
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.someSize];
self.physicsBody.contactBitMask = enemyCategory | powerUpCategory;
self.physicsBody.categoryBitMask = arbitraryNodeCategory;
self.physicsBody.dynamic = YES
}
return self;
}
- SKSpriteNode and SKPhysicsBody have other relevant properties you may need
SpriteKit transition between scenes without resetting game
You have lots of options to keep persistent state across your game scenes. I've listed two approaches I've used.
Option A: Maintain a Reference to the Scene
When a scene is swapped out for a new one, the scene is often fully removed from memory. If you hold onto a reference for the scene object somewhere else, and present that reference, no data will be lost.
To maintain a reference over time (and present the scene again when needed), I recommend a scene presenter class with a static instance such as the following:
class SceneCoordinator {
static var shared = SceneCoordinator()
var gameScene : GameScene?
var shopScene : ShopScene?
}
when you initialize a GameScene
, also register it with your with SceneCoordinator.shared.gameScene = self
. Then, when transitioning away from another scene, you can present the instance you stored in the coordinator class.
didMoveToView()
will still be called on the scene when it is presented again. You could move all your initializing code to a separate function, create a new instance var such as var isInitialized = false
, and only initialize your content if it is false (and set it to true after your initialize).
The issue with this approach is that scene objects are expensive, which means you could build up a large overhead by not allowing scenes to be released.
Option B: Model Struct
The better way (which would also allow for easier reinit of a scene after your app closes) would be to create a data model of your game state, and provide a function to create a GameScene from your model object.
This method is more consistent with the Model-View-Controller design pattern, and allows your scenes and data to be a lot more lightweight.
Such as:
struct GameModel {
var coins : Int
}
class GameScene : SKScene {
var state : GameModel
convenience init(size: CGSize, state: GameModel) {
self.state = state
// set up scene content from state
}
// lots of fun game scene logic
func playerCollectedCoin() {
state.coins += 1
}
func moveToShopScene() {
// init a new shop scene with the state of this scene
let shop = ShopScene(size: self.view!.bounds.size, state: self.state)
(self.view as! SKView).presentScene(scene)
}
}
class ShopScene : SKScene {
var state : GameModel
convenience init(size: CGSize, state: GameModel) {
self.state = state
// set up scene content from state
}
// lots of fun shop scene logic
func playerSpentCoins(amount: Int) {
state.coins -= amount
}
func moveToGameScene() {
// init a new game scene with the updated state of this scene
let game = GameScene(size: self.view!.size, state: self.state)
(self.view as! SKView).presentScene(game)
}
}
Switching Scenes in SpriteKit Swift 2
In my games, I use this:
// Setting a size (or else it may look like its zoomed in)
var selectView = BallSelect(size: view!.bounds.size)
// Setting a file for that scene
selectView = BallSelect(fileNamed: "BallSelect")!
// Setting the scale
selectView.scaleMode = SKSceneScaleMode.AspectFill
// Transitioning scenes wit a specific transition
self.scene?.view?.presentScene(selectView, transition: SKTransition.fadeWithColor(color, duration: 1))
Tell me if it works :)
Simplest Way To Change Scene In Swift + Sprite Kit
when changing between scenes, you're going to need to set up a transition, how the scene will change to the next and define which scene you want to transition to.
For the transition,
var transition:SKTransition = SKTransition.fadeWithDuration(1)
The fadeWithDuration
can be replaced with any SKTransition
a list of which can be found in the documentation https://developer.apple.com/library/prerelease/ios/documentation/SpriteKit/Reference/SKTransition_Ref/index.html
as for defining the scene,
var scene:SKScene = GameScene(size: self.size)
You're stating which scene you wish to transition to, in this case GameScene
, but should be replaced with whichever scene you wish to transition to.
In order to start the transition, call:
self.view?.presentScene(scene, transition: transition)
Which will move to the scene scene
which was set up in the line prior, using the transition transition
which was also defined.
Navigating between scenes in Sprite Kit?
Sprite Kit makes it easy to transition between scenes. You can either
keep scenes around persistently, or dispose of them when you
transition between them. In this example, you create a second scene
class to learn some other game behaviors. When the “Hello, World!”
text disappears from the screen, the code creates a new scene and
transitions to it. The Hello scene is discarded after the transition.
Sprite Kit Programming Guide
https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/GettingStarted/GettingStarted.html#//apple_ref/doc/uid/TP40013043-CH2-SW10
Swift spritekit change scenes?
It turns out I had a particle emitter in the menu scene. I forgot to remove it when transitioning. So it was trying to emitting to a scene that did not exist anymore. Fixing it was as simple as adding this line before transitioning to the game scene.
myparticle.removeFromParent();
Swift: Deallocate GameScene after transition to new scene?
In my project, I used the following mechanism and it worked well. All my scenes SKScene
objects were optional variables. When i need the new scene to show, i create it and present at SKView
. When i need to display the new scene, I set the previous object scene object to nil
, this immediately reducing the reference count by 1, and becouse of at this moment no one object is not use my scene, the reference count becomes zero and scene was deleted.
The SKScene
object is a ordinary class object and ARC
works with them like with all reference type objects. You only need to monitor the number of references to the scene. All are finished with the various resources I was start in deinit
of SKScene
object
The simple example:
At UIViewController
we have optional objects of GameScene
:
class GameViewController: UIViewController {
var scene1: GameScene?
var scene2: GameScene?
var skView: SKView?
// Action to present next Scene
@IBAction func nextSceneAction(sender: AnyObject) {
print("Next scene")
// Create new GameScene object
scene2 = GameScene(fileNamed:"GameScene")
// Present scene2 object that replace the scene1 object
skView!.presentScene(scene2)
scene = nil
}
override func viewDidLoad() {
super.viewDidLoad()
// Create GameScene object
scene = GameScene(fileNamed:"GameScene")
skView = (self.view as! SKView)
skView!.showsFPS = true
skView!.showsNodeCount = true
skView!.ignoresSiblingOrder = true
scene!.scaleMode = .AspectFill
// Present current scene
skView!.presentScene(scene)
}
}
At GameScene
in deinit
print some text to show that it object will be deleted:
class GameScene: SKScene {
...
deinit {
print("Deinit scene")
}
}
Debug output after push the nextSceneAction
button:
Next scene
Deinit scene
Swift: How to keep track of and communicate scores between scenes in SpriteKit
NSUserDefaults is a great way to keep track of scores.
To save the high score:
let x : Int = 45 // This int is your high score
var myString = String(x) // This String is you high score as a String
var defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(myString, forkey : "High_Score") // Saving the String to NSUserDefaults
To access the high score:
var defaults = NSUserDefaults.standardUserDefaults()
var HighScore = defaults.objectForKey("High_Score") // Retrieving your high score as a String
Related Topics
Accessing "Self" in Initializing Closure
Os X App Doesn't Launch New Window on Dock Icon Press in Swift
How to Detect When Two Objects Touch in Spritekit
Swift - Scanning with Ikscannerdeviceview on Osx
Multiple Scatterplots Using Core Plot and Swift
Error: Variable with Getter/Setter Cannot Have an Initial Value
Dealing with Octal Numbers in Swift
Swift: Check Which Value in Nsarray Is Closest to Another Given Value
In Swift, Dynamic Height for UItextview in UIcollectionview
Using Scenekit for Hittesting Not Returning a Hit with Scnnode
Swift 1.2 Assigning Let After Initialization
Drawing at Cocoa with Swift Creates an Error
How Does Swift Disambiguate Type Arguments in Expression Contexts
Problem with Frameworks in Command Line Tool
How to Reconstruct Grayscale Image from Intensity Values