Move SKSpriteNode position across multiple scenes - ios

I have two scenes with scenery where clouds (SKSpriteNodes) move on the x-axis. When I change scenes they reset to their initial position.
How can I pass the x-position to the new scene when I move scenes?
This is my code. Both scenes have the same code and structure.
Thanks, guys!
override func didMoveToView(view: SKView) {
addScene()
addMenuButtons()
addSocial()
}
override func update(currentTime: CFTimeInterval) {
if self.cloud01.position.x < self.cloud01.size.width * -1 {
self.cloud01.position.x = self.frame.size.width + (self.cloud01.size.width / 2)
} else {
self.cloud01.position.x -= 0.5
}
if self.cloud02.position.x < self.cloud02.size.width * -1 {
self.cloud02.position.x = self.frame.size.width + (self.cloud02.size.width / 2)
} else {
self.cloud02.position.x -= 0.3
}
if self.cloud03.position.x < self.cloud03.size.width * -1 {
self.cloud03.position.x = self.frame.size.width + (self.cloud03.size.width / 2)
} else {
self.cloud03.position.x -= 0.4
}
}
func addScene() {
self.cloud01.anchorPoint = CGPointMake(0.5, 0.5)
self.cloud01.size.width = self.frame.size.width / 3
self.cloud01.size.height = self.cloud01.size.width / 5
self.cloud01.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame) - 50)
self.cloud02.anchorPoint = CGPointMake(0.5, 0.5)
self.cloud02.size.width = self.frame.size.width / 3
self.cloud02.size.height = self.cloud01.size.width / 5
self.cloud02.position = CGPointMake(CGRectGetMaxX(self.frame) - 50, CGRectGetMaxY(self.frame) - 200)
self.cloud03.anchorPoint = CGPointMake(0.5, 0.5)
self.cloud03.size.width = self.frame.size.width / 3
self.cloud03.size.height = self.cloud01.size.width / 5
self.cloud03.position = CGPointMake(CGRectGetMinX(self.frame) + 50, CGRectGetMaxY(self.frame) - 125)
self.addChild(self.cloud01)
self.addChild(self.cloud02)
self.addChild(self.cloud03)
}

When you press Button from first Scene you can store cloud's positions into NSUserDefaults this way into touchBegan method:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if self.nodeAtPoint(location) == self.playButton {
let cloud1Pos = cloud01.position
let cloud2Pos = cloud02.position
let cloud3Pos = cloud03.position
NSUserDefaults().setObject(NSStringFromCGPoint(cloud1Pos), forKey: "cloud1Pos")
NSUserDefaults().setObject(NSStringFromCGPoint(cloud2Pos), forKey: "cloud2Pos")
NSUserDefaults().setObject(NSStringFromCGPoint(cloud3Pos), forKey: "cloud3Pos")
let reveal = SKTransition.flipHorizontalWithDuration(0.5)
let letsPlay = playScene(size: self.size)
self.view?.presentScene(letsPlay, transition: reveal)
}
}
}
After that when new scene is load into view the you can read stored values:
let cloud1Pos = CGPointFromString(NSUserDefaults().objectForKey("cloud1Pos") as! String)
let cloud2Pos = CGPointFromString(NSUserDefaults().objectForKey("cloud2Pos") as! String)
let cloud3Pos = CGPointFromString(NSUserDefaults().objectForKey("cloud3Pos") as! String)
Now you have all cloud's position so you can assign that position to your cloud:
func addScene() {
self.cloud01.anchorPoint = CGPointMake(0.5, 0.5)
self.cloud01.size.width = self.frame.size.width / 3
self.cloud01.size.height = self.cloud01.size.width / 5
self.cloud01.position = cloud1Pos
self.cloud02.anchorPoint = CGPointMake(0.5, 0.5)
self.cloud02.size.width = self.frame.size.width / 3
self.cloud02.size.height = self.cloud01.size.width / 5
self.cloud02.position = cloud2Pos
self.cloud03.anchorPoint = CGPointMake(0.5, 0.5)
self.cloud03.size.width = self.frame.size.width / 3
self.cloud03.size.height = self.cloud01.size.width / 5
self.cloud03.position = cloud3Pos
self.addChild(self.cloud01)
self.addChild(self.cloud02)
self.addChild(self.cloud03)
}
And One suggestion for you. Don't update cloud position from Update method instead of that you can make one function like updatePosOfCloud and you can run that function into didMoveToView method this way:
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(updatePosOfCloud), SKAction.waitForDuration(0.05)])))
I have did this in my sample project.
HERE is complete working project.
EDIT:
As Paulw11 suggested you can do it this way too:
declare global variables above the class declaration of your first scene:
var cloud1Position : CGPoint?
var cloud2Position : CGPoint?
var cloud3Position : CGPoint?
This can store your clouds position and when button is pressed you can assign the position of clouds to this variables this way:
cloud1Position = cloud01.position
cloud2Position = cloud02.position
cloud3Position = cloud03.position
In your second scene you can assign position to clouds:
self.cloud01.position = cloud1Position!
self.cloud02.position = cloud2Position!
self.cloud03.position = cloud3Position!
you can use any way to achieve your requirement.

Related

(Swift SpriteKit) Rotate sprite in the direction of touch

On the screenshot provided, the red arrow and cross are just for demonstration purposes and are not in the game. I would like the sprite of the spaceship to face the direction of the ball it shoots.
Link to image
Here is my current code for touch location
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
var bullet = SKSpriteNode(imageNamed: "bullet")
bullet.position = cannon.position
bullet.size = CGSize(width: 35, height: 35)
//physics
bullet.physicsBody = SKPhysicsBody(circleOfRadius: bullet.size.width/2)
bullet.physicsBody?.categoryBitMask = PhysicsCategory.bullet
bullet.physicsBody?.collisionBitMask = PhysicsCategory.enemy
bullet.physicsBody?.contactTestBitMask = PhysicsCategory.enemy
bullet.name = "bullet"
bullet.physicsBody?.affectedByGravity = false
self.addChild(bullet)
var dx = CGFloat(location.x - cannon.position.x)
var dy = CGFloat(location.y - cannon.position.y)
let magnitude = sqrt(dx * dx + dy * dy)
dx /= magnitude
dy /= magnitude
let vector = CGVector(dx: 120.0 * dx, dy: 120.0 * dy) //adjust constant to increase impluse.
bullet.physicsBody?.applyImpulse(vector)
// I found this code bellow this comment, but it just moves the cannon's y position
let direction = SKAction.moveTo(
CGPointMake(
400 * -cos(bullet.zRotation - CGFloat(M_PI_2)) + bullet.position.x,
400 * -sin(bullet.zRotation - CGFloat(M_PI_2)) + bullet.position.y
),
duration: 0.8)
cannon.runAction(direction)
}
}
Here is the code I found which works perfectly.
Rotate a sprite to sprite position not exact in SpriteKit with Swift
let angle = atan2(location.y - cannon.position.y , location.x - cannon.position.x)
cannon.zRotation = angle - CGFloat(M_PI_2)
I had been working some time ago in something like what you want so, here is my results
first you need to use VectorMath.swift created by Nick Lockwood and this is my code to make my spider move towards user touch
import SpriteKit
import SceneKit
class GameScene: SKScene {
let sprite = SKSpriteNode(imageNamed:"Aranna")
var velocity = Vector2(x: 0, y: 0)
var positionV2D = Vector2(x: 0, y: 0)
var headingVector = Vector2(x: 0, y: 1)
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.text = "Hello, World!";
myLabel.fontSize = 45;
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
sprite.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
positionV2D = Vector2(point:sprite.position);
let testVector = Vector2(x: 10, y: 14);
velocity += testVector;
print(velocity.toString());
velocity += Vector2(x: 1, y: 1);
//velocity = velocity + testVector;
print(velocity.toString());
velocity *= 0.5;
velocity.printVector2D();
velocity = Vector2(x: 2, y: 2);
velocity.normalized();
velocity.printVector2D();
self.addChild(sprite)
}
func ToRad(grados:CGFloat) ->CGFloat
{
return ((CGFloat(M_PI) * grados) / 180.0)
}
func ToDeg(rad:CGFloat) ->CGFloat
{
return (180.0 * rad / CGFloat(M_PI))
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
let toTarget = Vec2DNormalize(Vector2(point:location) - positionV2D);
let angle2 = headingVector.angleWith(toTarget);
print(ToDeg(CGFloat(angle2)));
headingVector.printVector2D();
self.sprite.runAction(SKAction.rotateToAngle(CGFloat(angle2), duration: 0.1))
self.sprite.runAction(SKAction.moveTo(location, duration: 0.5))
positionV2D = Vector2(point: location);
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
I hope this helps you
If you want to rotate only you shouldn't call the moveTo action.
Calculate the angle between touch location and location of the cannon and call action rotateToAngel
here is the code
let angle = atan2(dy, dx) - CGFloat(M_PI_2)
let direction = SKAction.rotateToAngle(angle, duration: 0.4, shortestUnitArc: true)
cannon.runAction(direction)
I found that with the answer submitted by James that this caused it to rotate in the wrong way, fine if you have something like a canonball but as it was used on my game which had a mage and a fireball with a trail it caused an issue, this can be easily fixed with
let angle = atan2(touchlocation.x - cannon.position.x , touchlocation.y -
cannon.position.y)
cannon.zRotation = -(angle - CGFloat(Double.pi/2))
use Double.pi / 2 and M_PI_2 is depriciated now

ios - Swift 2.2 - SpriteKit - sublassing SKSpriteNode class

For a puzzle game project on iOS, I try to subclass the SKSpriteNode class from SpriteKit with the following code:
class SKPuzzle: SKSpriteNode {
var name2:String = "";
}
I need to add other variables in SKSpriteNode like another name (name2) in this case. Here is the use I made of the class in a class type SKScene:
class GameScene: SKScene {
let background = SKSpriteNode(imageNamed: "BW")
var selectedNode = SKPuzzle()
override init(size: CGSize) {
super.init(size: size)
let imageNames = [sheet.Puzzle13() , sheet.Puzzle19(),sheet.Puzzle30(),
sheet.Puzzle11(), sheet.Puzzle29(), sheet.Puzzle35() ]
for i in 0..<imageNames.count {
let imageName = imageNames[i]
let sprite = SKPuzzle(texture: imageName)
sprite.name = kAnimalNodeName
sprite.name2 = "\(i)"
let offsetFraction = (CGFloat(i) + 1.0)/(CGFloat(imageNames.count) + 1.0)
sprite.position = CGPoint(x: size.width * offsetFraction, y: size.height / 2)
sprite.zPosition = 1
background.addChild(sprite)
}
}
I have the sprite object from the subclass SKPuzzle wich contains the new variable name2.
sprite.name2 = "\(i)"
The problem I have is the variable selectedNode (created with)
var selectedNode = SKPuzzle()
used later in the program contain always a nil value for the data name and name2. When I click on the jigsaw parts of the game, I get the following error:
fatal error: unexpectly found nil while unwrapping an optional value in the following function:
func panForTranslation(translation : CGPoint) {
let position = selectedNode.position
if selectedNode.name! == kAnimalNodeName {
selectedNode.position = CGPoint(x: position.x + translation.x * 2, y: position.y + translation.y * 2)
}
}
selectedNode seems containing only nil values. The code is working fine when I just use the SKSpriteNode but failed with my SKPuzzle class.
Here is the whole code of the program:
import SpriteKit
import UIKit
private let kAnimalNodeName = "puzzle"
private let kdancing = "dancing"
class SKPuzzle: SKSpriteNode {
var name2:String = "";
}
class GameScene: SKScene {
let background = SKSpriteNode(imageNamed: "BW")
var selectedNode = SKPuzzle()
var selectedVideo = SKVideoNode()
override init(size: CGSize) {
super.init(size: size)
// 1
self.background.name = kdancing
self.background.anchorPoint = CGPointZero
background.zPosition = 0
self.addChild(background)
//background.play()
// 2
let sheet = Statiques()
let sprite_dancing1 = SKSpriteNode(texture: sheet.Dancing1())
let sprite_dancing2 = SKSpriteNode(texture: sheet.Dancing2())
sprite_dancing1.name = kdancing
sprite_dancing2.name = kdancing
sprite_dancing1.position = CGPoint(x: 837, y: 752)
sprite_dancing1.zPosition = 1
sprite_dancing2.position = CGPoint(x: 1241, y: 752)
sprite_dancing2.zPosition = 1
background.addChild(sprite_dancing1)
background.addChild(sprite_dancing2)
let imageNames = [sheet.Puzzle13() , sheet.Puzzle19(), sheet.Puzzle30(), sheet.Puzzle11(), sheet.Puzzle29(), sheet.Puzzle35() ]
for i in 0..<imageNames.count {
let imageName = imageNames[i]
let sprite = SKPuzzle(texture: imageName)
sprite.name = kAnimalNodeName
sprite.name2 = "\(i)"
let offsetFraction = (CGFloat(i) + 1.0)/(CGFloat(imageNames.count) + 1.0)
sprite.position = CGPoint(x: size.width * offsetFraction, y: size.height / 2)
sprite.zPosition = 1
background.addChild(sprite)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let positionInScene = touch.locationInNode(self)
selectNodeForTouch(positionInScene)
}
}
override func didMoveToView(view: SKView) {
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: Selector("handlePanFrom:"))
self.view!.addGestureRecognizer(gestureRecognizer)
}
func handlePanFrom(recognizer : UIPanGestureRecognizer) {
if recognizer.state == .Began {
var touchLocation = recognizer.locationInView(recognizer.view)
touchLocation = self.convertPointFromView(touchLocation)
self.selectNodeForTouch(touchLocation)
} else if recognizer.state == .Changed {
var translation = recognizer.translationInView(recognizer.view!)
translation = CGPoint(x: translation.x, y: -translation.y)
self.panForTranslation(translation)
recognizer.setTranslation(CGPointZero, inView: recognizer.view)
} else if recognizer.state == .Ended {
}
}
func degToRad(degree: Double) -> CGFloat {
return CGFloat(degree / 180.0 * M_PI)
}
func selectNodeForTouch(touchLocation : CGPoint) {
// 1
let touchedNode = self.nodeAtPoint(touchLocation)
if touchedNode is SKPuzzle {
// 2
if !selectedNode.isEqual(touchedNode) {
selectedNode.removeAllActions()
selectedNode.runAction(SKAction.rotateToAngle(0.0, duration: 0.1))
//selectedNode = touchedNode as! SKSpriteNode
// 3
if touchedNode.name! == kAnimalNodeName {
let sequence = SKAction.sequence([SKAction.rotateByAngle(degToRad(-4.0), duration: 0.1),
SKAction.rotateByAngle(0.0, duration: 0.1),
SKAction.rotateByAngle(degToRad(4.0), duration: 0.1)])
selectedNode.runAction(SKAction.repeatActionForever(sequence))
}
}
}
}
func panForTranslation(translation : CGPoint) {
let position = selectedNode.position
if selectedNode.name! == kAnimalNodeName {
selectedNode.position = CGPoint(x: position.x + translation.x * 2, y: position.y + translation.y * 2)
}
}
}
Thanks by advance for your help, I'm beginning to code with Swift on iOS.
When selectedNode is created by SKPuzzle(), name is nil.
You have to set selectedNode.name to some value in init method.

Different sprite position with the same code

I have a weird issue with my sprite position, I tried clean build, restart xcode, and run in different schemes(iPhone5s, iPhone6), they all return the same strange issue.
I tried to set the position of the sprite by:
balls.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + self.frame.size.width)
so when I println(balls.position) to the console, it returns (0.0, 0.0)
But when I tried
println(CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + self.frame.size.width)
it returns (512.0,1408.0), and this is correct position where the ball should be.
I'm having issue with the last function, func ballPosition, it is used to determind the position of the sprite "balls". for some reason it is always (0, 0).
Here are the complete code from my test project:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var circle = SKShapeNode()
var balls = SKShapeNode()
var ballColor = ["red", "blue", "green", "yellow"]
var points = ["up", "down", "left", "right"]
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.whiteColor()
// set circle position and size
circle = SKShapeNode(circleOfRadius: 100 ) // Size of Circle
circle.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame)) //Middle of Screen
circle.strokeColor = SKColor.whiteColor()
circle.fillColor = SKColor.orangeColor()
self.addChild(circle)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
circleRotate()
ballPosition()
// test ball position, this is the part with the issue I mentioned above.
println(balls.position) // (0, 0)
println(CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + self.frame.size.width)) // (512.0,1408.0)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func circleRotate() {
let circleAction = SKAction.rotateByAngle(CGFloat(-M_PI * 2 / 3), duration: 0.1)
circle.runAction(SKAction.repeatAction(circleAction, count: 1))
}
func ballMove() {
let ballMovement = SKAction.moveTo(CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)), duration: 5)
balls.runAction(ballMovement)
}
func randomColor() {
let ballColorIndex = Int(arc4random_uniform(UInt32(ballColor.count)))
balls = SKShapeNode(circleOfRadius: 10 )
if ballColorIndex == 0 {
balls.strokeColor = SKColor.whiteColor()
balls.fillColor = SKColor.redColor()
// balls.zPosition = 10
ballMove()
} else if ballColorIndex == 1 {
balls.strokeColor = SKColor.whiteColor()
balls.fillColor = SKColor.blueColor()
// balls.zPosition = 10
ballMove()
} else if ballColorIndex == 2 {
balls.strokeColor = SKColor.whiteColor()
balls.fillColor = SKColor.greenColor()
// balls.zPosition = 10
ballMove()
} else if ballColorIndex == 3 {
balls.strokeColor = SKColor.whiteColor()
balls.fillColor = SKColor.yellowColor()
// balls.zPosition = 10
ballMove()
}
}
func ballPosition() {
let ballPointIndex = Int(arc4random_uniform(UInt32(points.count)))
if ballPointIndex == 0 {
balls.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + self.frame.size.width)
randomColor()
self.addChild(balls)
} else if ballPointIndex == 1 {
balls.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) - self.frame.size.height)
randomColor()
self.addChild(balls)
} else if ballPointIndex == 2 {
balls.position = CGPoint(x:CGRectGetMidX(self.frame) - self.frame.size.width, y:CGRectGetMidY(self.frame))
randomColor()
self.addChild(balls)
} else if ballPointIndex == 3 {
balls.position = CGPoint(x:CGRectGetMidX(self.frame) + self.frame.size.width, y:CGRectGetMidY(self.frame))
randomColor()
self.addChild(balls)
}
}
}
The problem is you're overwriting ball in randomColor() - you're creating a new node which hasn't been added to the parent view.
The underlying problem is you've structured your code in a way that leads to confusion and mistakes. You should keep your functions single purposed. They should do what they say they do. A function called randomColor() should not move the move the ball or set the ball size. The position function should not set the color.
I rearranged the code and ran it. That bug is fixed. You can see what I changed and should be able to get further now. Good luck.
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var circle = SKShapeNode()
var ball = SKShapeNode(circleOfRadius: 10)
var ballColor = ["red", "blue", "green", "yellow"]
var points = ["up", "dowm", "left", "right"]
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.whiteColor()
circle = SKShapeNode(circleOfRadius: 100 ) // Size of Circle
circle.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame)) //Middle of Screen
circle.strokeColor = SKColor.whiteColor()
circle.fillColor = SKColor.orangeColor()
self.addChild(circle)
self.addChild(ball)
ball.position = CGPointMake(150, 0)
println("Initial Ball Pos: \(ball.position)")
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
circleRotate()
setRandomBallPosition()
setRandomColor()
ballMove()
// test ball position
println("--------------")
println("Ball Pos: \(ball.position)")
println("Circle pos: \(circle.position)")
println("Midpoint: \(CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + self.frame.size.width))")
println("--------------")
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func circleRotate() {
let circleAction = SKAction.rotateByAngle(CGFloat(-M_PI * 2 / 3), duration: 0.1)
circle.runAction(SKAction.repeatAction(circleAction, count: 1))
}
func ballMove() {
let ballMovement = SKAction.moveTo(CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)), duration: 5)
ball.runAction(ballMovement)
}
func setRandomColor() {
ball.strokeColor = SKColor.whiteColor()
let ballColorIndex = Int(arc4random_uniform(UInt32(ballColor.count)))
switch(ballColorIndex) {
case 0:
ball.fillColor = SKColor.redColor()
case 1:
ball.fillColor = SKColor.blueColor()
case 2:
ball.fillColor = SKColor.greenColor()
case 3:
ball.fillColor = SKColor.yellowColor()
default:
println("Unexpected random index value ", ballColorIndex)
}
}
func setRandomBallPosition() {
let ballPointIndex = Int(arc4random_uniform(UInt32(points.count)))
println("ballPointIndex = \(ballPointIndex)")
switch(ballPointIndex) {
case 0:
ball.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + (self.frame.size.width / 2))
case 1:
ball.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) - (self.frame.size.height / 2))
case 2:
ball.position = CGPoint(x:CGRectGetMidX(self.frame) - (self.frame.size.width / 2), y:CGRectGetMidY(self.frame))
case 3:
ball.position = CGPoint(x:CGRectGetMidX(self.frame) + (self.frame.size.width / 2), y:CGRectGetMidY(self.frame))
default:
println("Unexpected random index value: ", ballPointIndex)
}
println("ball position = \(ball.position)")
}
}

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."

Resources