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)))
Related
I am trying to make a game that starts after a 3 second delay. So I am trying to add a sequence, so that the startGame function is called in a sequence after a delay. I can then call that function with the delay included at the beginning, but I keep getting an error when I try the run the sequence at the end of the second function.
I have the start game function:
func startGame(){
let spawn = SKAction.run(createEnemy)
let wait = SKAction.wait(forDuration: 2)
let spawnSequence = SKAction.sequence([wait, spawn])
let spawnForever = SKAction.repeatForever(spawnSequence)
self.run(spawnForever)
}
and then I have another function that uses that function in a sequence to add a delay.
func beginGame(){
let countdown = SKAction.wait(forDuration: 3)
let startGame = SKAction.run(self.startGame)
let startSequence = SKAction.sequence([countdown, startGame])
**self.beginGame().run(startSequence)**
}
I then call the beginGame() function at the along with the setup function. along with the setup function at the end.
scene.setup()
scene.beginGame()
I am getting an "Value of tuple '()' has no member 'run'"
Sorry for the stupid question, I'm a beginner at swift.
Try
let spawn = SKAction.run { self.createEnemy() }
and
let startGame = SKAction.run { self.startGame() }
run block is a closure so it might need the brackets
try this...
func startGame() -> SKAction {
let spawn = SKAction.run { self.hello() }
let wait = SKAction.wait(forDuration: 2)
let spawnSequence = SKAction.sequence([wait, spawn])
let spawnForever = SKAction.repeatForever(spawnSequence)
return spawnForever
}
func beginGame() {
let countdown = SKAction.wait(forDuration: 3)
let startGame = self.startGame()
let startSequence = SKAction.sequence([countdown, startGame])
}
self.beginGame()
SKActions should be run on a node. Your line of code
self.beginGame().run(startSequence) appears to be trying to run your sequence on the function beginGame(). Because beginGame()which does not have a defined return type.
According to https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html
Functions without a defined return type return a special value of type
Void. This is simply an empty tuple, which is written as ().
and as the compiler is telling you, you cannot use run on a tuple.
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.
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
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.
I am trying make a game something like Tower defense. I am adding units to my map and I want to try movepoint to another point.
My code is:
func setTheNode(position:[Int],speed:[Int]){
let moveDown1 = SKAction.moveToY(300, duration:4)
let moveRight1 = SKAction.moveToX(300, duration: 3)
let sequence = SKAction.sequence([moveDown1, moveRight1]);
self.runAction(sequence)
}
This works, however I want to read these position and durations from parameters.
When I try this way:
func setTheNode(position:[Int],speed:[Int]){
let moveDown1 = SKAction.moveToY(position[0], duration:speed[0])
let moveRight1 = SKAction.moveToY(position[1], duration:speed[1])
let sequence = SKAction.sequence([moveDown1, moveRight1]);
self.runAction(sequence)
}
I ger the error:
Cannot invoke 'moveToY' with an argument list of type '(Int, duration:
Int)'
I also tried
let moveDown1 = SKAction.moveToY(position[0] as? Int, duration:speed[0] as? Int)
And got the same error again. Could you help me on this?
Thank you.
SKAction.moveToY accept a CGFloat for Y and an NSTimeInterval for duration. Therefore you need to change position's type to [CGFloat] and speed's type to [NSTimeInterval].
There are a few other things I notice with your code:
1. I think it's a typo - you've written let moveRight1 = SKAction.moveToY in your second function. Should that be SKAction.moveToX?
2. You're using speed to represent time. That's going to give you some strange results because speed and time are inversely proportional. Therefore, with your implementation, if you increase speed it's going to take longer for your node to move...
I would recommend writing your function like so:
func setTheNode(deltaPosition dp: CGVector, times: (x: NSTimeInterval, y: NSTimeInterval)){
let moveDown1 = SKAction.moveToY(dp.dx, duration: times.x)
let moveRight1 = SKAction.moveToX(dp.dy, duration: times.y)
let sequence = SKAction.sequence([moveDown1, moveRight1]);
self.runAction(sequence)
}