SpriteKit - How to transition to a new scene with a timer - swift

I am currently trying to build a loading scene into my SpriteKit game with my developer logo on it and need to transition to the MainMenuScene after say 5 seconds. How would I go about that.
My code right now looks like this, which is basically just the background/logo image.
import SpriteKit
class LoadingScene: SKScene {
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "fatscoprion")
background.position = CGPoint (x: self.size.width / 2, y: self.size.height / 2)
background.zPosition = -1
self.addChild(background)
}
}

You can create a Scheduled Timer and configure a function to call your method that's creates and present the new scene.
Example:
class LoadingScene: SKScene {
var timer = Timer()
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "")
background.position = CGPoint (x: self.size.width / 2, y: self.size.height / 2)
background.zPosition = -1
self.addChild(background)
//Create a Scheduled timer thats will fire a function after the timeInterval
timer = Timer.scheduledTimer(timeInterval: 5.0,
target: self,
selector: #selector(presentNewScene),
userInfo: nil, repeats: false)
}
#objc func presentNewScene() {
//Configure the new scene to be presented and then present.
let newScene = SKScene(size: .zero)
view?.presentScene(newScene)
}
deinit {
//Stops the timer.
timer.invalidate()
}
}

Related

Whats wrong with #objc func addAlien?

Im getting 2 errors in line #objc func addAlien() (#objc can only be used with members of classes) and gameTimer = Timer.scheduledTimer(timeInterval: 0.75, target: self, selector: #selector(addAlien), userInfo: nil, repeats: true) (use of local variable 'addAlian' before its declaration)
I'm sorry, i'm new in Swift developming, any ideas how i can fix it?
Thanks.
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var starfield: SKEmitterNode!
var player: SKSpriteNode!
var scoreLabel: SKLabelNode!
var score: Int = 0{
didSet {
scoreLabel.text = "Счет \(score)"
}
}
var gameTimer: Timer!
var aliens = ["alien" , "alien2" , "alien3 "]
override func didMove(to view: SKView) {
starfield = SKEmitterNode (fileNamed: "Starfield") // Connect animation to starfield
starfield.position = CGPoint(x: 0, y: 1472) // Position starfield on screen
starfield.advanceSimulationTime(10)
self.addChild(starfield) // Add starfield on screen
starfield.zPosition = -1
player = SKSpriteNode(imageNamed: "damir2") // Подключаем картинку игрока
player.position = CGPoint (x: 0, y: -300) // Позиция игрока
self.addChild(player) // add player
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0) // Delete gravitation from the game
self.physicsWorld.contactDelegate = self
scoreLabel = SKLabelNode(text: "Score: 0") // Score table
scoreLabel.fontName = "AmericanTypewriter-Bold" // Шрифт для таблички
scoreLabel.fontSize = 56
scoreLabel.fontColor = UIColor.white
scoreLabel.position = CGPoint(x: -200, y: 500)
score = 0
self.addChild(scoreLabel)
gameTimer = Timer.scheduledTimer(timeInterval: 0.75, target: self, selector: #selector(addAlien), userInfo: nil, repeats: true)
#objc func addAlien(){
aliens = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: aliens) as! [String]
let alien = SKSpriteNode(imageNamed: aliens[0])
let randomPos = GKRandomDistribution(lowestValue: 20, highestValue: 350)
let pos = CGFloat(randomPos.nextInt())
alien.position = CGPoint(x: pos, y: -800)
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
According to code you shared - you declared addAlien inside of the didMove function. You need to move addAlien out of the didMove function and put it same level with didMove - in a class scope.

How to make object move farther longer you hold down?

I am trying to create a program where the longer you hold down the screen the higher an object will move. I want to do it where the longer you hold down the longer the function repeats increasing the distance. However, for some reason it is not working.
import SpriteKit
import GameplayKit
var calculateTimer = Timer()
var distance = 10
var choice = 1
class GameScene: SKScene, SKPhysicsContactDelegate {
var planet = SKNode()
override func didMove(to view: SKView) { // viewdidload
let planetTexture = SKTexture(imageNamed: "mercury.jpg")
planet = SKSpriteNode(texture: planetTexture)
planet.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
addChild(planet)
}
func calculateDistance() {
distance += 10
print(distance)
print(choice)
}
func touchDown(atPoint pos : CGPoint) {
calculateTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.calculateDistance), userInfo: nil, repeats: true)
print("test")
}
func touchUp(atPoint pos : CGPoint) {
planet.physicsBody!.isDynamic = true
//planet.physicsBody?.velocity = (CGVector(dx: 0, dy: 0))
planet.physicsBody!.applyImpulse(CGVector(dx: 0, dy: distance))
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
This is my code. If anyone has any suggestions they would be much appreciated!
In touchDown, set a flag to indicate a touch has started.
In touchUp, clear/remove the flag.
Whilst the flag is set, keep performing your movement code.

check if an instance of a spritenode is at a certain posion on the screen

Im trying to make it so that the program will print something to the console when an instance of the sprite is in the center of the screen from top to bottom
var sprite = SKSpriteNode()
override func didMoveToView(view: SKView) {
//spawns an instance of the sprite
NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: Selector("spawnObject"), userInfo: nil, repeats: true)
//call the check middle function
checkMiddle()
}
//spawns a sprite
func spawnObject() {
let size = CGSizeMake(self.frame.size.width/3, self.frame.size.width3)
self.physicsWorld.gravity = CGVectorMake(0, -CGFloat(objectSpeed))
sprite = SKSpriteNode(imageNamed: "spriteImage")
sprite.position = CGPointMake(CGFloat(100), self.frame.height)
sprite.size = size
sprite.physicsBody = SKPhysicsBody(circleOfRadius: self.frame.size.width)
sprite.physicsBody?.affectedByGravity = true
addChild(self.sprite)
}
//PRINT WHEN SPRITE IS IN THE CENTER OF SCREEN FROM TOP TO BOTTOM
func checkMiddle() {
if sprite.position.y == self.frame.size.height/2 {
print("center")
}
}
if this is for debugging purposes you can use :
override func update(currentTime: NSTimeInterval)
{
checkMiddle()
}
The update() function is called before every frame so you need to be careful what you put in there
If you don't need it to be checked as frequently as that (60 x per second), you could also use an action:
let checkCenterAction = SKAction.runBlock(){ self.checkMiddle() }
let waitAction = SKAction.waitForDuration( 0.1 ) // every 1/10th of a second
let checkAndWaitAction = SKAction.sequence([checkCenterAction, waitAction])
sprite.runAction(SKAction.repeatForever(checkAndWaitAction))
And yet another option is to use the SKPhysicsBody to detect collisions

Spawn enemies randomly only within the screen width

I started working with SKS files to create games in swift, and in this game im trying to get enemies to spawn randomly within the width of the phone screen as opposed to all over the sks file scene
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
NSTimer.scheduledTimerWithTimeInterval(0.2, target: self, selector: Selector("spawnEnemy"), userInfo: nil, repeats: true)
}
func spawnEnemy(){
//supposed to pick random point within the screen width
let xPos = Int.random(self.frame.width)
enemy = SKSpriteNode(imageNamed: "enemy")
enemy.position = CGPointMake(CGFloat(xpos), self.frame.size.height/2)
enemy.physicsBody = SKPhysicsBody(circleOfRadius: 7)
enemy.physicsBody?.affectedByGravity = true
enemy.physicsBody?.categoryBitMask = 0
enemy.physicsBody?.contactTestBitMask = 1
addChild(self.enemy)
}
Anyways, I wrote an example for you :)
So, here is how your example should look in order to work:
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
NSTimer.scheduledTimerWithTimeInterval(0.2, target: self, selector: Selector("spawnEnemy"), userInfo: nil, repeats: true)
}
func randomBetweenNumbers(firstNum: CGFloat, secondNum: CGFloat) -> CGFloat{
return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum - secondNum) + min(firstNum, secondNum)
}
func spawnEnemy(){
//supposed to pick random point within the screen width
let xPos = randomBetweenNumbers(0, secondNum: frame.width )
let enemy = SKSpriteNode(imageNamed: "enemy") //create a new enemy each time
enemy.position = CGPointMake(CGFloat(xPos), self.frame.size.height/2)
enemy.physicsBody = SKPhysicsBody(circleOfRadius: 7)
enemy.physicsBody?.affectedByGravity = true
enemy.physicsBody?.categoryBitMask = 0
enemy.physicsBody?.contactTestBitMask = 1
addChild(enemy)
}
Method randomBetweenNumbers is borrowed from here.
And, this is another way of how you can spawn enemies by using SKAction:
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
let wait = SKAction .waitForDuration(1, withRange: 0.5)
let spawn = SKAction.runBlock({
self.spawnEnemy()
})
let spawning = SKAction.sequence([wait,spawn])
self.runAction(SKAction.repeatActionForever(spawning), withKey:"spawning")
}
Method spawnEnemy remains the same in both cases. To stop spawning you can remove an action for certain key ("spawning" in this case). You can do it like this:
if((self.actionForKey("spawning")) != nil){
self.removeActionForKey("spawning")
}

Sprite kit rotation repeat

How do you make a sprite rock back and forth constantly like a boat?
Heres the code i wrote:
import UIKit
import SpriteKit
let boat = SKSpriteNode(imageNamed: "boat_floor")
var rotationVal = CGFloat(-1)
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
backgroundColor = UIColor.whiteColor()
boat.size = CGSize(width: self.frame.size.width, height: self.frame.size.width)
boat.position = CGPoint(x: self.frame.size.width/2, y: 0)
boat.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "boat_floor"), size: boat.size)
boat.physicsBody?.dynamic = false
boat.alpha = 1
self.addChild(boat)
ChangeAngle()
NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("ChangeAngle"), userInfo: nil, repeats: true)
}
func ChangeAngle(){
let action = SKAction.rotateByAngle(rotationVal, duration:30)
if rotationVal < 0 {
rotationVal = CGFloat(M_PI)
boat.runAction(action)
}
else if rotationVal > 0 {
rotationVal = -CGFloat(M_PI)
boat.runAction(action)
}
print(rotationVal)
}
}
If I understand you correctly, you can do it like this (just copy&paste the code to see how it works):
import UIKit
import SpriteKit
let boat = SKSpriteNode(color: SKColor.greenColor(), size:CGSize(width: 200, height: 80))
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
backgroundColor = UIColor.whiteColor()
boat.position = CGPoint(x: self.frame.size.width/2, y: 100)
boat.alpha = 1
self.addChild(boat)
let rotate = SKAction.rotateByAngle(-0.6, duration:4)
let sequence = SKAction.repeatActionForever(SKAction.sequence([rotate, rotate.reversedAction()]))
let complete = SKAction.sequence([SKAction.rotateByAngle(0.3, duration:2), sequence])
boat.runAction(complete, withKey:"someKey")
}
}
Using NSTimer is generally bad idea in SpriteKit because it don't respect node's, scene's or view's paused state.