Why does SpriteKit crash when transitioning back and forth between scenes - ios

I checked the answer here and on other sites but didn't get a definitive solution.
I have two scenes
Scene 1:
class GameMenuScene: SKScene {
override func didMoveToView(view: SKView) {
// Add background
var background: SKSpriteNode = SKSpriteNode(imageNamed: "Starfield")
background.position = CGPointMake(-20, 0)
background.size = CGSizeMake(self.size.width + 40, self.size.height)
background.anchorPoint = CGPointZero
background.blendMode = SKBlendMode.Replace
self.addChild(background)
// Add game title
gameTitle = SKSpriteNode(imageNamed: "Title")
gameTitle.name = "Game Title"
gameTitle.xScale = scale
gameTitle.yScale = scale
gameTitle.position = CGPoint(x: CGRectGetMidX(self.frame), y: self.frame.height * 0.75)
self.addChild(gameTitle)
// Add scoreboard
scoreboard = SKSpriteNode(imageNamed: "ScoreBoard")
scoreboard.name = "Scoreboard"
scoreboard.xScale = scale
scoreboard.yScale = scale
scoreboard.position = CGPoint(x: CGRectGetMidX(self.frame), y: self.frame.height * 0.50)
self.addChild(scoreboard)
// Add play button
playButton = SKSpriteNode(imageNamed: "PlayButton")
playButton.name = "PlayButton"
playButton.xScale = scale
playButton.yScale = scale
playButton.position = CGPoint(x: CGRectGetMidX(self.frame), y: self.frame.height * 0.25)
self.addChild(playButton)
// Add menu score label
var menuScoreLabel = SKLabelNode()
menuScoreLabel.fontName = fontName
menuScoreLabel.fontSize = scoreFontsize
menuScoreLabel.text = String(score)
menuScoreLabel.position = CGPointMake(scoreboard.position.x - (scoreboard.size.width / 4), scoreboard.position.y - (scoreboard.size.height / 4))
menuScoreLabel.zPosition = 10
self.addChild(menuScoreLabel)
// Add menu top score label
var menuTopScoreLabel = SKLabelNode()
menuTopScoreLabel.fontName = fontName
menuTopScoreLabel.fontSize = scoreFontsize
menuTopScoreLabel.text = String(userDefaults.integerForKey("TopScore"))
menuTopScoreLabel.position = CGPointMake(scoreboard.position.x + (scoreboard.size.width / 4), scoreboard.position.y - (scoreboard.size.height / 4))
menuTopScoreLabel.zPosition = 10
self.addChild(menuTopScoreLabel)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
// Get touch location
var touchLocation = touch.locationInNode(self)
// Check if Play button is presssed
if playButton.containsPoint(touchLocation) == true {
//println("right node touched")
// Transition to GameScene.swift
var transition: SKTransition = SKTransition.fadeWithDuration(1)
// Configure the view.
let scene = GameScene()
let skView = self.view! as SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.size = skView.bounds.size
scene.scaleMode = .AspectFill
skView.presentScene(scene, transition: transition)
}
}
}
}
Scene 2:
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
// Add background
var background: SKSpriteNode = SKSpriteNode(imageNamed: "Starfield")
background.position = CGPointMake(-20, 0)
background.size = CGSizeMake(self.size.width + 40, self.size.height)
background.anchorPoint = CGPointZero
background.blendMode = SKBlendMode.Replace
self.addChild(background)
// Add mainlayer & labelHolderLayer
self.addChild(mainLayer)
self.addChild(labelHolderLayer)
// Add cannon
cannon = SKSpriteNode(imageNamed: "Cannon")
cannon.name = "Cannon"
cannon.position = CGPoint(x: CGRectGetMidX(self.frame), y: 0)
self.addChild(cannon)
// Add score label
scoreLabel.name = "Score Label"
scoreLabel.fontName = fontName
scoreLabel.fontSize = scoreFontsize
scoreLabel.text = String(score)
scoreLabel.position = CGPointMake(CGRectGetMidX(self.frame), self.frame.size.height - scoreFontsize)
scoreLabel.zPosition = 10
self.addChild(scoreLabel)
// Add LivesDisplay
livesDisplay = SKSpriteNode(imageNamed: "Ammo5")
livesDisplay.name = "livesDisplay"
livesDisplay.position = CGPoint(x: self.size.width - livesDisplay.size.width, y: self.size.height - livesDisplay.size.height)
self.addChild(livesDisplay)
// Settings for new game
newGame()
}
func gameIsOver() {
// Set game over flag and stop movement
gameOver = 1
mainLayer.speed = 0
mainLayer.paused = true
// Add game over label
gameOverLabel.fontName = fontName
gameOverLabel.fontSize = gameOverFontsize
gameOverLabel.text = "Game Over!"
gameOverLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
gameOverLabel.zPosition = 10
labelHolderLayer.addChild(gameOverLabel)
// Set main menu top score
if score > userDefaults.integerForKey("TopScore") {
userDefaults.setInteger(score, forKey: "TopScore")
userDefaults.synchronize()
}
// Run acton sequence (wait a few seconds before transitioning)
runAction(SKAction.sequence([SKAction.waitForDuration(1), SKAction.runBlock({ () -> Void in
// Transition back to GameMenuScene
var transition: SKTransition = SKTransition.fadeWithDuration(1)
// Configure the view.
let scene = GameMenuScene()
let skView = self.view! as SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.size = skView.bounds.size
scene.scaleMode = .AspectFill
skView.presentScene(scene, transition: transition)
})]))
}
}
When I initially launch the app, I can transition from scene 1 to scene 2. when the game is over, the app transitions back to scene 1. BUT when I try to go to scene 2 again, the app crashes. This only happens on a device, on the simulator, i can go back and forth without any problems.
the error I get is that a SKNode is being added that already exists. I know what it means and the error starts when I try to
// Add mainlayer & labelHolderLayer
self.addChild(mainLayer)
self.addChild(labelHolderLayer)
But I don't see why It can add the background with no problem but crash from there on. Also why does it work on the simulator but not on a device? do I really need to check in my didMoveToView if the nodes are already created?

I found out the mistake I was making.
I was instantiating the nodes outside my scene 2 class, making them global. Therefore when I went from scene 1 -> scene 2 -> scene 1 there was not problem but going then to scene 2 again caused a crash because the nodes were globally created.
Solution:
Moving the following code within the class solved the problem.
var mainLayer = SKSpriteNode()
var labelHolderLayer = SKSpriteNode()
var livesDisplay = SKSpriteNode()
var scoreLabel = SKLabelNode()

Related

Passing Data Between Scenes (SpriteKit)

How do I pass information in SpriteKit from one scene to another? In my game I have two scenes, the GameScene and the GameOverScene. The score is displayed in the GameScene as it increases, but how do I send this information to the second scene?
This function is called when the player runs out of lives which changes the scene.
func changeScene(){
let secondScene = GameOverScene(size: self.size)
secondScene.scaleMode = scaleMode
let transition = SKTransition.fadeWithDuration(0.5)
self.view?.presentScene(secondScene, transition: transition)
}
This is my gameOverScene
class GameOverScene: SKScene {
var playAgainLabel = SKLabelNode(fontNamed:"Chalkduster")
var currentScore: Int
var scoreLabel = SKLabelNode(fontNamed: "Chalkduster")
var highScore = 0
var highScoreLabel = SKLabelNode(fontNamed: "Chalkduster")
var gameScene = GameScene()
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.blackColor()
playAgainLabel.text = "Click To Play Again!"
playAgainLabel.fontSize = 30
playAgainLabel.fontColor = SKColor.whiteColor()
playAgainLabel.position = CGPoint(x: frame.width / 2, y: frame.height / 2)
self.addChild(playAgainLabel)
if currentScore > highScore {
highScore = currentScore
}
scoreLabel.text = "Score: " + String(currentScore)
scoreLabel.fontSize = 20
scoreLabel.fontColor = SKColor.whiteColor()
scoreLabel.position = CGPoint(x: frame.width/2, y: frame.height/1.4)
self.addChild(scoreLabel)
highScoreLabel.text = "Best: " + String(highScore)
highScoreLabel.fontSize = 20
highScoreLabel.fontColor = SKColor.whiteColor()
highScoreLabel.position = CGPoint(x: frame.width / 2, y: frame.height / 1.6)
self.addChild(highScoreLabel)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let playingScene = GameScene(size: self.size)
playingScene.scaleMode = scaleMode
let fadeTransition = SKTransition.fadeWithDuration(0.5)
self.view?.presentScene(playingScene, transition: fadeTransition)
}
}
}
For instance, your GameOverScene could be something like this:
class GameOverScene: SKScene {
var object: SomeObject!
}
Now, in your changeScene:
func changeScene(){
let secondScene = GameOverScene(size: self.size)
secondScene.scaleMode = scaleMode
secondScene.object = somethingInFirstSceneThatNeedToBePassed //here we do the passing
let transition = SKTransition.fadeWithDuration(0.5)
self.view?.presentScene(secondScene, transition: transition)
}
Subclass SKScene and create a new initializer with arguments that you want to pass. As you transition from GameScene to second scene, create second scene by using the initializer you created and pass the score. Then present the second scene.

Scoring Method for Swift & Spawning objects

I cannot seem to work out how to get my scoring to work...
the concept of the game is birds will come in on the right hand side, and when they reach the left hand side i want my score to increment by 1.
In Obj_c i would have done something like
if (bird.enter.x < 0) {
score++
}
but in swift sprite kit I'm not to sure how to do it...
Another problem I'm having is that i have to use a timer to get my "Enemy Birds" to spawn (the birds going from right to left)
(every 1 second the timer runs the spawn enemy function) but in Obj_c i would have spawned say 3/4 at different x co-ordinates and like the previous code when the bird was < 0 px i would CGPointMake() back over to the other side of the screen so they are on a constant loop
Any ideas of doing this in swift? My code is as follows: its in bit of a weird layout:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var bg = SKSpriteNode() // Create Background
var bird = SKSpriteNode() // Create User Bird
var scoreLabel = SKLabelNode() // Create Score Label
var score: Int = 0 // Create Score Integer
var enemyBird = SKSpriteNode() // Create Enemy Bird
var birdGroup:UInt32 = 1 // Bird Collision Group
var objectGroup:UInt32 = 2 // Enemy Collision Group
var scoreGroup:UInt32 = 3 // Score Collision Group
var gameOver = 0 // Game Over function
var movingObjects = SKNode() // ??
var gameOverLabel = SKLabelNode() // Game over label
var labelHolder = SKSpriteNode() // Holds Label - Game Over
var Menu = 0
/* Put Bird in and animate */
var birdTexture = SKTexture(imageNamed: "bird_img_1.png")
var birdTexture2 = SKTexture(imageNamed: "bird_img_2.png")
var birdTexture3 = SKTexture(imageNamed: "bird_img_3.png")
var birdTexture4 = SKTexture(imageNamed: "bird_img_4.png")
var birdDeadTexture = SKTexture(imageNamed: "bird_img_dead.png")
var finalSpawn: CGFloat = 0.0
/* ------------------------------------ Main Setup ------------------------------------------ */
override func didMoveToView(view: SKView) {
/* Setup your scene here */
println("Moved to Game Scene")
movingObjects.speed = 0
/* Call Functions */
backgroundImage()
enemySpawn()
/* Set up deletates and physics and timer */
self.physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVectorMake(0, -5)
self.addChild(movingObjects)
self.addChild(labelHolder)
/* Load Score Text */
scoreLabel.fontName = "Helvetica"
scoreLabel.fontSize = 60
scoreLabel.text = "0"
scoreLabel.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)+self.frame.size.height*0.3)
self.addChild(scoreLabel)
enemyBird.position = CGPointMake(self.frame.width+200, 500)
movingObjects.addChild(enemyBird)
bird = SKSpriteNode(texture: birdTexture)
bird.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
// Make bird smaller
bird.size.height = bird.size.height/12
bird.size.width = bird.size.width/11
// Animate Bird
var animation = SKAction.animateWithTextures([birdTexture, birdTexture2, birdTexture3, birdTexture4], timePerFrame: 0.08)
var makeBirdFlap = SKAction.repeatActionForever(animation)
bird.runAction(makeBirdFlap)
// Load Physics
// birdPhysics()
// Run bird animation
bird.zPosition = 10
self.addChild(bird)
/* Introduce ground and top into the scene */
// Create Ground
var ground = SKSpriteNode()
ground.position = CGPointMake(CGRectGetMidX(self.frame), self.frame.size.height/100 * 16)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, 1))
ground.physicsBody?.dynamic = false
ground.physicsBody?.categoryBitMask = objectGroup
// Add Ground to scene
self.addChild(ground)
// Create Top
var top = SKSpriteNode()
top.position = CGPointMake(0, self.frame.size.height)
top.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, 1))
top.physicsBody?.dynamic = false
top.physicsBody?.categoryBitMask = objectGroup
// Add Top to scene
self.addChild(top)
}
/* ------------------------------------ Spawning Enemy Birds ------------------------------------------ */
/* Create and Spawn enemy birds */
func enemySpawn() {
let height = self.frame.size.height // screen height as variable
let width = self.frame.size.width // screen width as variable
var bottomBarHeight = self.frame.size.height*0.16 // bottom bar height
var spawningPoint = height - bottomBarHeight // screen height - 16%
var randSpawn = arc4random_uniform(UInt32(spawningPoint)) // random number between 0 and 645
finalSpawn = CGFloat(randSpawn) + bottomBarHeight // add 16% (bottombarheight to the random number
println(Int(finalSpawn))
if (enemyBird.position.y > frame.size.height) {
enemyBird.position.y = frame.size.height - 100
}
if (enemyBird.position.y < self.frame.size.width * 0.16) {
enemyBird.position.y = self.frame.size.width + 0.18
}
// Create enemy bird
var enemyBirdTexture = SKTexture(imageNamed: "enemy_img_2.png")
// enemyBird.position = CGPointMake(self.frame.width+200, 500)
enemyBird = SKSpriteNode(texture: enemyBirdTexture)
enemyBird.size.height = enemyBird.size.height/12
enemyBird.size.width = enemyBird.size.width/11
enemyBird.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(enemyBird.size.width/1.1, enemyBird.size.height/1.1))
enemyBird.physicsBody?.dynamic = false
enemyBird.physicsBody?.categoryBitMask = objectGroup
// BIRD LOOP - ONLY RUNNING ONCE???
let startPoint = CGPointMake(self.frame.width+200, finalSpawn)
let endPoint = CGPointMake(0, finalSpawn)
let moveToEndAction = SKAction.moveTo(endPoint, duration: 2)
let resetTostartAction = SKAction.moveTo(startPoint, duration: 0)
let moveToEndThenStartAgain = SKAction.repeatActionForever(SKAction.sequence([moveToEndAction,resetTostartAction]))
enemyBird.runAction(SKAction.repeatActionForever(moveToEndThenStartAgain))
}
/* ------------------------------------ Other Parts ------------------------------------------ */
func backgroundImage() {
/* Put Background Image In */
// Create Background Texture
var bgTexture = SKTexture(imageNamed: "bg.png")
// Link bg variable to texture
bg = SKSpriteNode(texture: bgTexture)
// Position thr bg image
bg.position = CGPoint(x: CGRectGetMidX(self.frame), y: self.frame.height/2)
bg.size.height = self.frame.height
// Move background image left
var moveBg = SKAction.moveByX(-bgTexture.size().width, y: 0, duration: 9)
var replaceBg = SKAction.moveByX(bgTexture.size().width, y: 0, duration: 0)
var moveBgForever = SKAction.repeatActionForever(SKAction.sequence([moveBg, replaceBg]))
// Keep world never ending
for var i:CGFloat = 0; i < 3; i++ {
// Position Background
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: bgTexture.size().width/2 + bgTexture.size().width * i, y: CGRectGetMidY(self.frame))
// Stretch background full height of screen
bg.size.height = self.frame.height
// Run the action to move BG
bg.runAction(moveBgForever)
self.addChild(bg)
}
}
/* ------------------------------------ Create Bird Physics ------------------------------------------ */
func birdPhysics() {
/* Give bird physics */
// Give bird physics
bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.size.height/3)
bird.physicsBody?.mass = 0.2
bird.physicsBody?.dynamic = true
bird.physicsBody?.allowsRotation = false
bird.physicsBody?.categoryBitMask = birdGroup
bird.physicsBody?.collisionBitMask = objectGroup
bird.physicsBody?.contactTestBitMask = objectGroup
}
/* ------------------------------------ If contact is found ------------------------------------------ */
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == objectGroup || contact.bodyB.categoryBitMask == objectGroup {
bird.removeAllActions()
bird.texture = birdDeadTexture
let action = SKAction.rotateByAngle(CGFloat(-M_PI), duration:1)
bird.runAction(SKAction.repeatActionForever(action))
if gameOver == 0 {
movingObjects.speed = 0
gameOver = 1
movingObjects.removeAllChildren()// Remove all enemies
gameOverLabel.fontName = "Helvetica"
gameOverLabel.fontSize = 25
gameOverLabel.text = "Tap to retry!"
gameOverLabel.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)*1.5)
labelHolder.addChild(gameOverLabel)
gameOverLabel.zPosition = 9
}
}
}
/* ------------------------------------ When user touches the screen ------------------------------------------ */
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
if (Menu == 0) {
movingObjects.speed = 1
birdPhysics()
let action = SKAction.rotateByAngle(CGFloat(-M_PI), duration:1)
bird.runAction(SKAction.repeatActionForever(action))
}
if (gameOver == 0) { // Runs if game is not over
bird.physicsBody?.velocity = CGVectorMake(0, 0)
bird.physicsBody?.applyImpulse(CGVectorMake(0, 80))
Menu = 1 // Number on right is jump height
} else { // Runs if game is over
score = 0 // Score int is 0
scoreLabel.text = "0" // Score Label is 0
bird.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)) // Position Bird in center
bird.physicsBody?.velocity = CGVectorMake(0,0) // Cannot make bird jump
labelHolder.removeAllChildren() // Removes all labels
gameOver = 0 // Sets game over to 0 so game will run
movingObjects.speed = 1
bird.texture = birdTexture
// Animate Bird
var animation = SKAction.animateWithTextures([birdTexture, birdTexture2, birdTexture3, birdTexture4], timePerFrame: 0.08)
var makeBirdFlap = SKAction.repeatActionForever(animation)
bird.runAction(makeBirdFlap)
let height = self.frame.size.height
let width = self.frame.size.width
var gameScene: GameScene = GameScene(size: CGSizeMake(width, height))
var spriteView: SKView = self.view as SKView!
var trans :SKTransition = SKTransition.crossFadeWithDuration(0.5)
spriteView.presentScene(gameScene, transition: trans)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if (enemyBird.position.x == 0) {
score = score + 1
scoreLabel.text = "\(score)"
}
if (enemyBird.position.y > frame.size.height) {
enemyBird.position.y = frame.size.height - enemyBird.size.height
}
} }
You can increase the score like
if (bird.position.x < 0)
{
score++
}
And to loop the animation of enemy birds, you can make a looping SKAction from one end to other end. For example like this
let startPoint = CGPointMake(0, 200)
let endPoint = CGPointMake(self.frame.width, 200)
let moveToEndAction = SKAction.moveTo(endPoint, duration: 2)
let resetTostartAction = SKAction.moveTo(startPoint, duration: 0)
let moveToEndThenStartAgain = SKAction.repeatActionForever(SKAction.sequence([moveToEndAction,resetTostartAction]))
node.runAction(SKAction.repeatActionForever(moveToEndThenStartAgain))
To start at a random height each time, you can use
let maxHeight:UInt32 = 400
let startPoint = CGPointMake(0, CGFloat(arc4random() % maxHeight))
let moveToEndAction = SKAction.moveByX(self.frame.width, y: 0, duration: 1.0)
let resetTostartAction = SKAction.runBlock { () -> Void in
let randomHeight = CGFloat(arc4random() % maxHeight)
self.playButton.position = CGPointMake(startPoint.x,randomHeight)
}
let moveToEndThenStartAgain = SKAction.repeatActionForever(SKAction.sequence([moveToEndAction,resetTostartAction]))
enemyBird.runAction(SKAction.repeatActionForever(moveToEndThenStartAgain))
You can change maxHeight as required.

Display Current Score in Game using Sprite Kit and Swift

I have been working on a Flappy Bird type game for a couple of days now in Sprite Kit. I am new to programming so I have been trying to teach myself to make the game through various online videos and tutorials. I have come to the point where I need to create a scoring system that will display the current score on the screen in real time. I have looked and looked for a good tutorial on this but have had no luck finding one. I have a score label set up already in the code below. I have included below my entire scene. All I need to know is how to update the score label text each time the bird flies through a pipe. Any advice or suggestions would be greatly appreciated, thank you.
//
// ArcheryScene.swift
// FlappyBird (swift)
//
// Created by Brandon Ballard on 1/6/15.
// Copyright (c) 2015 Brandon Ballard. All rights reserved.
//
import UIKit
import SpriteKit
class ArcheryScene: SKScene, SKPhysicsContactDelegate {
var bird = SKSpriteNode()
var pipeUpTexture = SKTexture()
var pipeDownTexture = SKTexture()
var pipesMoveAndRemove = SKAction()
var impulse = 1
var count = 0
var scoreLabel = SKLabelNode()
let scoreLabelName = "scoreLabel"
let pipeGap = 150.0
enum ColliderType:UInt32 {
case BIRD = 1
case PIPE = 2
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
backgroundColor = SKColor.cyanColor()
//physics
self.physicsWorld.gravity = CGVectorMake(0.0, -15.0);
self.physicsWorld.contactDelegate = self
//Bird
var birdTexture = SKTexture(imageNamed:"Bird")
birdTexture.filteringMode = SKTextureFilteringMode.Nearest
bird = SKSpriteNode(texture: birdTexture)
bird.setScale(0.6)
bird.position = CGPoint(x: self.frame.width * 0.35 + 20, y: self.frame.size.height * 0.95)
bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.size.height / 2.0)
bird.physicsBody?.dynamic = true
bird.physicsBody?.allowsRotation = true
bird.physicsBody?.affectedByGravity = true
bird.physicsBody!.collisionBitMask = ColliderType.BIRD.rawValue
bird.physicsBody!.contactTestBitMask = ColliderType.PIPE.rawValue
self.addChild(bird)
//Ground
var groundTexture = SKTexture(imageNamed: "Ground")
var sprite = SKSpriteNode(texture: groundTexture)
sprite.setScale(2.0)
sprite.position = CGPointMake(self.size.width / 2, sprite.size.height / 2.0)
self.addChild(sprite)
var ground = SKNode()
ground.position = CGPointMake(0, groundTexture.size().height + 0)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, groundTexture.size().height * 2.0))
ground.physicsBody?.dynamic = false
self.addChild(ground)
//Score Label
scoreLabel = SKLabelNode(fontNamed: "ScoreLabel")
scoreLabel.name = scoreLabelName
scoreLabel.fontSize = 125
scoreLabel.fontColor = SKColor.whiteColor()
scoreLabel.text = "\(count)"
println(size.height)
scoreLabel.position = CGPointMake(frame.size.width / 2, frame.size.height / 14)
self.addChild(scoreLabel)
//Pipes
//Create the Pipes
pipeUpTexture = SKTexture(imageNamed: "PipeUp")
pipeDownTexture = SKTexture(imageNamed: "PipeDown")
//Movement of Pipes
let distanceToMove = CGFloat(self.frame.size.width + 2.0 * pipeUpTexture.size().width)
let movePipes = SKAction.moveByX(-distanceToMove, y: 0.0, duration: NSTimeInterval(0.01 * distanceToMove))
let removePipes = SKAction.removeFromParent()
pipesMoveAndRemove = SKAction.sequence([movePipes,removePipes])
//Spawn Pipes
let spawn = SKAction.runBlock({() in self.spawnPipes()})
let delay = SKAction.waitForDuration(NSTimeInterval(2.0))
let spawnThenDelay = SKAction.sequence([spawn,delay])
let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay)
self.runAction(spawnThenDelayForever)
}
func spawnPipes() {
let pipePair = SKNode()
pipePair.position = CGPointMake(self.frame.size.width + pipeUpTexture.size().width * 2, 0)
pipePair.zPosition = -10
let height = UInt32(self.frame.size.height / 4)
let y = arc4random() % height + height
var pipeDown = SKSpriteNode(texture: pipeDownTexture)
pipeDown.setScale(2.0)////////
pipeDown.position = CGPointMake(3.0, CGFloat(y) + pipeDown.size.height + CGFloat(pipeGap) )
pipeDown.physicsBody = SKPhysicsBody(rectangleOfSize: pipeDown.size)
pipeDown.physicsBody?.dynamic = false
pipeDown.physicsBody!.affectedByGravity = false
pipeDown.physicsBody!.collisionBitMask = ColliderType.PIPE.rawValue
pipePair.addChild(pipeDown)
var pipeUp = SKSpriteNode(texture: pipeUpTexture)
pipeUp.setScale(2.0)
pipeUp.position = CGPointMake(0.0, CGFloat(y))
pipeUp.physicsBody = SKPhysicsBody(rectangleOfSize: pipeUp.size )
pipeUp.physicsBody?.dynamic = false
pipeUp.physicsBody!.affectedByGravity = false
pipeUp.physicsBody!.collisionBitMask = ColliderType.PIPE.rawValue
pipePair.addChild(pipeUp)
pipePair.runAction(pipesMoveAndRemove)
self.addChild(pipePair)
}
func didBeginContact(contact: SKPhysicsContactDelegate) {
impulse = 0
let fadeOut = SKAction.sequence([SKAction.waitForDuration(3.0)])
let welcomeReturn = SKAction.runBlock({
let Transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
let welcomeScene = GameScene(fileNamed: "GameScene")
welcomeScene.scaleMode = .AspectFill
self.scene!.view?.presentScene(welcomeScene, transition: Transition)
})
let sequence = SKAction.sequence([fadeOut, welcomeReturn])
self.runAction(sequence)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if bird.position.y > (self.frame.size.height * 0.999){
impulse = 0
}
if impulse == 1 {
bird.physicsBody?.velocity = CGVectorMake( 0, 0 )
bird.physicsBody?.applyImpulse(CGVectorMake(0,25))
}
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Just set up an invisible sprite between your pipes. when your bird passes through it, you can detect that and increase your score.
you can make an invisible sprite like this
let scoreSprite = SKSpriteNode(color: SKColor.clearColor(), size: theSize)
add a physics body to that, and youre good to go.

My Sprites Are Scrunched in Sprite Kit using Swift

I am creating my first game with Sprite Kit. It is a Flappy Bird type game. The image on the left is what the game looked like earlier today. I ended up creating an entirely new project. I copied the code for the previous project and pasted it into the current one. I placed the same exact images as the previous project into the current one as well. There are no compiling errors and everything works the same except that the game now looks like the image on the right. As you can see the images widths are smaller. Any advice would be greatly appreciated, if you would like me to provide all of the code in this question let me know, thank you.
This is all of the code for the Archery Scene:
//
// ArcheryScene.swift
// FlappyBird (swift)
//
// Created by Brandon Ballard on 1/6/15.
// Copyright (c) 2015 Brandon Ballard. All rights reserved.
//
import UIKit
import SpriteKit
class ArcheryScene: SKScene, SKPhysicsContactDelegate {
var bird = SKSpriteNode()
var pipeUpTexture = SKTexture()
var pipeDownTexture = SKTexture()
var pipesMoveAndRemove = SKAction()
var score = 0
let pipeGap = 150.0
enum ColliderType:UInt32 {
case BIRD = 1
case PIPE = 2
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
backgroundColor = SKColor.cyanColor()
//physics
self.physicsWorld.gravity = CGVectorMake(0.0, -15.0);
self.physicsWorld.contactDelegate = self
//Bird
var birdTexture = SKTexture(imageNamed:"Bird")
birdTexture.filteringMode = SKTextureFilteringMode.Nearest
bird = SKSpriteNode(texture: birdTexture)
bird.setScale(0.6)
bird.position = CGPoint(x: self.frame.width * 0.35 + 20, y: self.frame.size.height * 0.95)
bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.size.height / 2.0)
bird.physicsBody?.dynamic = true
bird.physicsBody?.allowsRotation = true
bird.physicsBody?.affectedByGravity = true
bird.physicsBody!.collisionBitMask = ColliderType.BIRD.rawValue
bird.physicsBody!.contactTestBitMask = ColliderType.PIPE.rawValue
self.addChild(bird)
//Ground
var groundTexture = SKTexture(imageNamed: "Ground")
var sprite = SKSpriteNode(texture: groundTexture)
sprite.setScale(2.0)
sprite.position = CGPointMake(self.size.width / 2, sprite.size.height / 2.0)
self.addChild(sprite)
var ground = SKNode()
ground.position = CGPointMake(0, groundTexture.size().height + 0)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, groundTexture.size().height * 2.0))
ground.physicsBody?.dynamic = false
self.addChild(ground)
//Pipes
//Create the Pipes
pipeUpTexture = SKTexture(imageNamed: "PipeUp")
pipeDownTexture = SKTexture(imageNamed: "PipeDown")
//Movement of Pipes
let distanceToMove = CGFloat(self.frame.size.width + 2.0 * pipeUpTexture.size().width)
let movePipes = SKAction.moveByX(-distanceToMove, y: 0.0, duration: NSTimeInterval(0.01 * distanceToMove))
let removePipes = SKAction.removeFromParent()
pipesMoveAndRemove = SKAction.sequence([movePipes,removePipes])
//Spawn Pipes
let spawn = SKAction.runBlock({() in self.spawnPipes()})
let delay = SKAction.waitForDuration(NSTimeInterval(2.0))
let spawnThenDelay = SKAction.sequence([spawn,delay])
let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay)
self.runAction(spawnThenDelayForever)
}
func spawnPipes() {
let pipePair = SKNode()
pipePair.position = CGPointMake(self.frame.size.width + pipeUpTexture.size().width * 2, 0)
pipePair.zPosition = -10
let height = UInt32(self.frame.size.height / 4)
let y = arc4random() % height + height
var pipeDown = SKSpriteNode(texture: pipeDownTexture)
pipeDown.setScale(2.0)////////
pipeDown.position = CGPointMake(3.0, CGFloat(y) + pipeDown.size.height + CGFloat(pipeGap) )
pipeDown.physicsBody = SKPhysicsBody(rectangleOfSize: pipeDown.size)
pipeDown.physicsBody?.dynamic = false
pipeDown.physicsBody!.affectedByGravity = false
pipeDown.physicsBody!.collisionBitMask = ColliderType.PIPE.rawValue
pipePair.addChild(pipeDown)
var pipeUp = SKSpriteNode(texture: pipeUpTexture)
pipeUp.setScale(2.0)
pipeUp.position = CGPointMake(0.0, CGFloat(y))
pipeUp.physicsBody = SKPhysicsBody(rectangleOfSize: pipeUp.size )
pipeUp.physicsBody?.dynamic = false
pipeUp.physicsBody!.affectedByGravity = false
pipeUp.physicsBody!.collisionBitMask = ColliderType.PIPE.rawValue
pipePair.addChild(pipeUp)
pipePair.runAction(pipesMoveAndRemove)
self.addChild(pipePair)
}
func didBeginContact(contact: SKPhysicsContactDelegate) {
scene?.view?.paused = true
//gameOver()
}
func createScoreNode() -> SKLabelNode {
let scoreNode = SKLabelNode(fontNamed: "Brandon Ballard")
scoreNode.name = "scoreNode"
let newScore = "\(score)"
scoreNode.text = newScore
scoreNode.fontSize = 125
scoreNode.fontColor = SKColor.cyanColor()
scoreNode.position = CGPointMake(CGRectGetMidX(self.frame), 58)
self.addChild(scoreNode)
return scoreNode
}
func gameOver() {
let scoreNode = self.createScoreNode()
self.addChild(scoreNode)
let fadeOut = SKAction.sequence([SKAction.waitForDuration(3.0), SKAction.fadeOutWithDuration(3.0)])
let welcomeReturn = SKAction.runBlock({
let transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
let welcomeScene = GameScene(fileNamed: "GameScene")
self.scene!.view?.presentScene(welcomeScene, transition: transition)
})
let sequence = SKAction.sequence([fadeOut, welcomeReturn])
self.runAction(sequence)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
bird.physicsBody?.velocity = CGVectorMake( 0, 0 )
bird.physicsBody?.applyImpulse(CGVectorMake(0,25))
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
All I needed to do to fix my problem was add a simple line of code to my GameScene file. Here is the code I had in the GameScene file before I solved the problem that presents the ArcheryScene:
welcomeNode?.runAction(fadeAway, completion: {
let doors = SKTransition.pushWithDirection(SKTransitionDirection.Down, duration: 1.0)
let archeryScene = ArcheryScene(fileNamed: "ArcheryScene")
self.view?.presentScene(archeryScene, transition: doors)
})
All I had to do was add this line of code after I created "archeryScene":
archeryScene.scaleMode = .AspectFill
This made the images "fit to screen size". The exact description of ".AspectFill" used by Apple is:
"The scaling factor of each dimension is calculated and the larger of the two is chosen. Each axis of the scene is scaled by the same scaling factor. This guarantees that the entire area of the view is filled but may cause parts of the scene to be cropped."

Scene transition in spritekit not transitioning

I am in sprite kit making a game in swift. (I am new to swift) And I would like to transition a scene when a node is pressed. I looked it up and the proper way to transition is
let transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
let scene = GameScene(size: self.scene.size)
scene.scaleMode = SKSceneScaleMode.AspectFill
self.scene.view.presentScene(scene, transition: transition)
But when I am programming it doesn't like the self.scene.size and puts a question mark on scene. Here is my code
let playView = PlayScene(size: self.scene?.size)
And I don't know how to fix this. Any suggestions? (Or it might be just a simple error in my part because I am new to swift.
Here is more of my code
In the GameScene.swift File
class GameScene: SKScene {
var playButton = SKSpriteNode(imageNamed: "Play")
var levelsButton = SKSpriteNode(imageNamed: "Levels")
var optionsButton = SKSpriteNode(imageNamed: "Options")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let myLabel = SKLabelNode(fontNamed:"Helvetica-Light")
myLabel.text = "Mini Golf";
myLabel.fontSize = 30;
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:525);
println(size.height)
println(size.width)
let backgroundNode = SKSpriteNode(imageNamed: "Background")
backgroundNode.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame));
playButton.position = CGPoint(x: CGRectGetMidX(self.frame), y: 360)
levelsButton.position = CGPoint(x: CGRectGetMidX(self.frame), y: 295)
optionsButton.position = CGPoint(x: CGRectGetMidX(self.frame), y: 230)
self.addChild(backgroundNode)
self.addChild(playButton)
self.addChild(levelsButton)
self.addChild(optionsButton)
self.addChild(myLabel)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
super.touchesBegan(touches, withEvent: event)
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
//let touchedNode = nodeAtPoint(Location)
//touchedNode.postiion =location
let transitionToPlayScene = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
if CGRectContainsPoint(playButton.frame, location) {
let playView = PlayScene(size: self.scene?.size)
}
}
}
This is the code in the PlayScene.swift file
import Foundation
import UIKit
import SpriteKit
private let WALL_CATEGORY: UInt32 = 0x1 << 0
private let HOLE_CATEGORY: UInt32 = 0x1 << 1
private let GOLF_CLUB_CATEGORY: UInt32 = 0x1 << 2
private let BALL_CATEGORY: UInt32 = 0x1 << 4
private let DECORATION_BORDER_CATEGORY: UInt32 = 0x1 << 5
class PlayScene: SKScene, SKPhysicsContactDelegate {
var ballNode = SKSpriteNode(imageNamed: "Ball")
var flag = SKSpriteNode(imageNamed: "Flag")
var club = SKSpriteNode(imageNamed: "Golf Club")
var tee = SKSpriteNode(imageNamed: "Tee")
var hole = SKSpriteNode(imageNamed: "Hole")
var decoBorder1 = SKSpriteNode(imageNamed: "Border 1")
var decoBorder2 = SKSpriteNode(imageNamed: "Border 2")
override func didMoveToView(view: SKView) {
//Setting up the view's physics
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame);
self.physicsBody = borderBody
self.physicsBody?.friction = 0.0
self.physicsBody?.categoryBitMask = WALL_CATEGORY
self.physicsWorld.gravity = CGVectorMake(0.0 ,0.0);
self.physicsWorld.contactDelegate = self
//Setting the ball's properties
ballNode.position = CGPoint(x: CGRectGetMidX(self.frame), y: 100);
ballNode.physicsBody = SKPhysicsBody(rectangleOfSize: ballNode.frame.size);
ballNode.physicsBody?.categoryBitMask = BALL_CATEGORY
ballNode.physicsBody?.mass = 50.0
ballNode.physicsBody?.dynamic = true
ballNode.physicsBody?.allowsRotation = true
self.addChild(ballNode)
//Setting the club's properties
club.position = CGPoint(x: CGRectGetMidX(self.frame)-20, y: 75);
club.physicsBody = SKPhysicsBody(rectangleOfSize: self.frame.size);
club.physicsBody?.categoryBitMask = GOLF_CLUB_CATEGORY
club.physicsBody?.mass = 150.0
club.physicsBody?.dynamic = true
club.physicsBody?.allowsRotation = false
self.addChild(club)
//Setting the Tee's properties
tee.position = CGPoint(x: CGRectGetMidX(self.frame), y: 70);
//self.addChild(tee)
//Setting the decoration border nodes
decoBorder1.position = CGPoint(x: CGRectGetMinX(self.frame), y: 500);
decoBorder2.position = CGPoint(x: CGRectGetMaxX(self.frame), y:500);
decoBorder1.physicsBody = SKPhysicsBody(rectangleOfSize: self.frame.size);
decoBorder2.physicsBody = SKPhysicsBody(rectangleOfSize: self.frame.size);
decoBorder1.physicsBody?.categoryBitMask = DECORATION_BORDER_CATEGORY
decoBorder1.physicsBody?.dynamic = false;
decoBorder1.physicsBody?.allowsRotation = false
decoBorder2.physicsBody?.categoryBitMask = DECORATION_BORDER_CATEGORY
decoBorder2.physicsBody?.dynamic = false;
decoBorder2.physicsBody?.allowsRotation = false
//self.addChild(decoBorder1)
//self.addChild(decoBorder2))
//Setting the Hole properties
hole.position = CGPoint(x: CGRectGetMidX(self.frame), y: 550);
hole.physicsBody = SKPhysicsBody(rectangleOfSize: self.frame.size);
hole.physicsBody?.categoryBitMask = HOLE_CATEGORY
hole.physicsBody?.dynamic = false
hole.physicsBody?.allowsRotation = false
//self.addChild(hole)
}
The question mark comes from optional chaining because your scene property must have been declared like this SKScene?. This isn't a problem though, it just means that if your scene is nil, it won't get the size property.
The real problem is probably this line:
self.scene.view.presentScene(scene, transition: transition)
Reason being, your scene probably doesn't have a view before it's presented.
You probably wanted something like this:
(self.view as SKView).presentScene(scene, transition: transition)
You'll notice I said probably a lot. I'm only speculating on the problem because I don't see enough code, but I appreciate the effort to try and narrow down your problem. You should post more though because it may be somewhere else.
Edit
Try replacing this:
let transitionToPlayScene = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
if CGRectContainsPoint(playButton.frame, location) {
let playView = PlayScene(size: self.scene?.size)
}
With this
if CGRectContainsPoint(playButton.frame, location) {
let transitionToPlayScene = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
let nextScene = PlayScene(size: self.size)
self.view!.presentScene(nextScene, transition: transitionToPlayScene)
}
What ended up working for me was this:
let transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
let scene = GameScene(size: (self.scene as SKScene!).size)
scene.scaleMode = SKSceneScaleMode.AspectFill
(self.view as SKView!).presentScene(scene, transition: transition)

Resources