How to wait for a duration before a sequence is run? - swift

I'm making a game with SpriteKit, and when it starts, I have nodes that spawn and fall from the top of the screen:
let wait = SKAction.waitForDuration(0.2, withRange: 0.19)
let spawn = SKAction.runBlock {
self.addTears()
}
let sequence = SKAction.sequence([wait, spawn])
self.runAction(SKAction.repeatActionForever(sequence))
Before these nodes spawn, I want to wait for a duration of 1 second, but only when the game starts. I tried to add a waitForDuration before I run the sequence but it didn't work.

Try:
let otherWait = SKAction.waitForDuration(1)
let otherSequence = SKAction.sequence([otherWait, SKAction.repeatActionForever(sequence)])
runAction(otherSequence)

Related

How to get different random delays in a SpriteKit sequence?

I have a sequence where i spawn a obstacle and then wait for a random amount of time, but if I run the game and for example the first random delay 1.4 seconds, but its not just for the first delay it's just all the time 1.4 and it doesn't change (it doesn't have to be 1.4 it's just an example). I have tried to make a function which has a random return value but its doesn't work. I have no idea how i could solve this. Here's my Code for the function with the random return value. If it helps obstSwitch() is the function that creates the Obstacle:
func getRandomDelay() ->Double {
let randomNumber = arc4random_uniform(20) + 5
let randomDelay: Double = Double(randomNumber) / 10
print(randomDelay)
return randomDelay
}
and heres the function that get's called when the game started:
func gameStarted() {
gameAbleToStart = false
moveLine()
scoreTimer()
let runObstSwitch = SKAction.run {
self.obstSwitch()
}
let wait = SKAction.wait(forDuration: getRandomDelay())
let sequence = SKAction.sequence([runObstSwitch, wait])
self.run(SKAction.repeatForever(sequence))
}
let wait = SKAction.wait(forDuration: getRandomDelay())
let sequence = SKAction.sequence([runObstSwitch, wait])
creates the wait action once, which is then used in the sequence,
so the same amount of idle time is spent between the runObstSwitch
actions.
If you want the idle time to be variable, use
wait(forDuration:withRange:) instead. For example with
let wait = SKAction.wait(forDuration: 1.5, withRange: 2.0)
let sequence = SKAction.sequence([runObstSwitch, wait])
the delay will be a random number between 1.5-2.0/2 = 0.5 and 1.5+2.0/2 = 2.5 seconds, varying for each execution.

Code inside SKAction run action not executing

The code block inside a SKAction.run action never executes for some reason.
To clarify, it's the two lines inside of startAction that never run for some reason even though the other lines do run.
Putting breakpoints on those lines prove those lines never execute.
Any clue why?
// Set first frame
let firstFrame = frames[0]
let animationNode = SKSpriteNode(texture: firstFrame)
animationNode.position = CGPoint(x: x, y: y)
// Set start action
let startAction = SKAction.run({
gAudio.playSound(file: .TestSound) // Never runs
self.animationLayer.addChild(animationNode) // Never runs
})
// Set rest of animation
let timePerFrame = 0.5
let animationAction = SKAction.animate(with: frames, timePerFrame: timePerFrame, resize: false, restore: true)
let removeAction = SKAction.removeFromParent()
let animationSequence = SKAction.sequence([startAction, animationAction, removeAction])
// Run animation
animationNode.run(animationSequence)
Actions will not fire for a node until it is placed on the scene, you have a chicken and egg dilemma going on here. You want to add the node (egg) to the scene (chicken) after the node (egg) exists in the world (chicken gives birth to the same egg). You need to have somethings else place the node on the scene, then the node will be able to run the actions.
Place your start action on your scene, and not your node, and it should start running

swift spritekit increase frequency of node creation as time goes on

I have figured out how to continuously spawn a node every x seconds. However, I would like to decrease the time that I wait to create a node as the game goes on, to increase the difficulty. For example, I call this function in didMoveToView:
func createSequentialEnemies(){
runAction(SKAction.repeatActionForever(
SKAction.sequence([
SKAction.runBlock(createEnemy),
SKAction.waitForDuration(2.0)
])
))
}
This creates an enemy every 2 seconds, but I want to decrease this duration arbitrarily. For example, say that after 30 seconds of gameplay I want to now spawn enemies every 1.5 seconds. How would I change the duration dynamically?
Create a spawnDuration property and key reference in your scene class.
class SomeClass: SKScene {
private var spawnDuration: NSTimeInterval = 2.0
private let spawnKey = "SpawnKey"
}
Than adjust your spawn code to use this spawn property and key. I slightly changed the syntax as well to make it more legible in my opinion.
func createSequentialEnemies(){
removeActionForKey(spawnKey) // remove previous action if running. This way you can adjust the spawn duration property and call this method again and it will cancel previous action.
let spawnAction = SKAction.runBlock(createEnemy)
let spawnDelay = SKAction.waitForDuration(spawnDuration)
let spawnSequence = SKAction.sequence([spawnAction, spawnDelay])
runAction(SKAction.repeatActionForever(spawnSequence), withKey: spawnKey) // run action with key so you can cancel it later
}
Than you have to add some logic of when you want to change the spawn duration property you created.
Time based could be a func like this you also call once in DidMoveToView
func startDifficultyTimer() {
let difficultyTimerKey = "DifficultyTimerKey"
let action1 = SKAction.waitForDuration(30)
let action2 = SKAction.runBlock { [unowned self] in
guard self.spawnDuration > 0.2 else { // set a min limit
removeActionForKey(difficultyTimerKey) // if min duration has been reached than you might as well stop running this timer.
return
}
self.spawnDuration -= 0.5 // reduce by half a second
self.createSequentialEnemies() // spawn enemies again
}
let sequence = SKAction.sequence([action1, action2])
runAction(SKAction.repeatActionForever(sequence), withKey: difficultyTimerKey)
}
Hope this helps

Wait For Duration Decreasing Parameter

I want to decreasing the parameter of SKAction.waitForDuration(X).
I'm using the Flappy Bird Tutorial (https://www.youtube.com/watch?v=RjUvEiNxWfc - see on minute 7) and I want to do something like
var timeToWait:Int = 8
let spawn = SKAction.runBlock({
() in
self.createWalls()
timeToWait--
})
let delay = SKAction.waitForDuration(timeToWait)
let SpawnDelay = SKAction.sequence([spawn, delay])
let spawnDelayForever = SKAction.repeatActionForever(SpawnDelay)
self.runAction(spawnDelayForever)
However, the parameter for waitForDuration is NSTimer and not a float. What could I do to change that parameter?
Thank you!
waitForDuration expects a value of type NSTimeInterval.
You can Type cast timeToWait when you pass it to the function:
let delay = SKAction.waitForDuration(NSTimeInterval(timeToWait))
By the way, your code seems to want to decrease the delay between each spawn but it will not do that because timeDelay is captured in your creation of the delay action and will not change that action's duration if you modify the variable afterward.
[EDIT]
Here's an example accelerates spawning interval (from 8 to 1) in cycles:
let spawn = SKAction.runBlock({ self.createWalls() })
var spawnCycle:[SKAction] = []
for timeToWait in (1...8).reverse()
{
spawnCycle.append(spawn)
spawnCycle.append(SKAction.waitForDuration(NSTimeInterval(timeToWait)))
}
self.runAction(SKAction.repeatActionForever(SKAction.sequence(spawnCycle)))

Spawning a Spritekit node at a random time

I'm making a game where I have a node that is spawning and falling from the top of the screen. However I want to make the nodes spawn at random time intervals between a period of 3 seconds. So one spawns in 1 second, the next in 2.4 seconds, the next in 1.7 seconds, and so forth forever. I am struggling with what the code should be for this.
Code I currently have for spawning node:
let wait = SKAction.waitForDuration(3, withRange: 2)
let spawn = SKAction.runBlock { addTears()
}
let sequence = SKAction.sequence([wait, spawn])
self.runAction(SKAction.repeatActionForever(spawn))
The code for my addTears() function is:
func addTears() {
let Tears = SKSpriteNode (imageNamed: "Tear")
Tears.position = CGPointMake(Drake1.position.x, Drake1.position.y - 2)
Tears.zPosition = 3
addChild(Tears)
//gravity
Tears.physicsBody = SKPhysicsBody (circleOfRadius: 150)
Tears.physicsBody?.affectedByGravity = true
//contact
Tears.physicsBody = SKPhysicsBody (circleOfRadius: Tears.size.width/150)
Tears.physicsBody!.categoryBitMask = contactType.Tear.rawValue
Tears.physicsBody!.contactTestBitMask = contactType.Bucket.rawValue
}
It's not advisable that you use NSTimer with SpriteKit (see SpriteKit - Creating a timer). Instead, to generate random times you could use SKAction.waitForDuration:withRange:
Creates an action that idles for a randomized period of time.
When the action executes, the action waits for the specified amount of
time, then ends...
Each time the action is executed, the action computes a new random
value for the duration. The duration may vary in either direction by
up to half of the value of the durationRange parameter...
To spawn nodes at random times you could combine waitForDuration:withRange with runBlock: together in an SKAction sequence. For example:
// I'll let you adjust the numbers correctly...
let wait = SKAction.wait(forDuration: 3, withRange: 2)
let spawn = SKAction.run {
// Create a new node and it add to the scene...
}
let sequence = SKAction.sequence([wait, spawn])
self.run(SKAction.repeatForever(sequence))
// self in this case would probably be your SKScene subclass.