Step Counter Sprite Kit - swift

I am trying to implement a step counter in my sprite Kit game.
And it should work like this:
The counter adds 1 to a value each second.
Every fifth second the duration (in this case 1) gets divided by 1.1
But if I create a func that returns the new duration, the repeat forever SKAction only uses this value for one time and then the duration never changes again.

you should make an action that calls itself, rather than using SKAction.repeatForever(...). you can recalculate values that way. not sure i entirely understand your use case, but here is an example that fires an event after a duration, and modifies that duration every fifth cycle.
var isLoopEnabled:Bool = true
var counter:Int = 0
var duration:TimeInterval = 1.0
func updateDuration() {
duration /= 1.1
}
/*
creates an event loop. the action waits, fires, then calls itself again (before exiting)
turn the loop off using the isLoopEnabled flag
*/
func loop() {
let wait = SKAction.wait(forDuration: duration)
let run = SKAction.run {
self.counter += 1 //increment counter
//update duration every fifth count
if self.counter % 5 == 0 {
self.updateDuration()
}
}
let end = SKAction.run{
print("\(self.counter) -- duration: \(self.duration)")
guard self.isLoopEnabled else { return } //flag allows you to exit loop
self.loop() //repeats by calling itself
}
let sequence = SKAction.sequence([ wait, run, end ])
self.run(sequence, withKey:"loop action")
}
override func didMove(to view: SKView) {
loop()
}

Related

Swift for iOS - 2 for loops run at the same time?

I have two objects where I need to update their UI at the same time. I have a for loop for one, and after that another for loop. Each iteration in the for loop I have a short delay so that for elements in the object I am making a UI change... one after the other - not seemingly all at once.
func update(value: Int){
var delay: Double = 0.05
// first loop
for i in 0...value {
delayWithSeconds(delay) {
//do something with object 1
}
delay = delay + 0.05
}
var delay2: Double = 0.05
// second loop
for i in 0...value {
delayWithSeconds(delay2) {
//do something with object 2
}
delay2 = delay2 + 0.05
}
}
// Utility
func delayWithSeconds(_ seconds: Double, completion: #escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
completion()
}
}
I have tried wrapping each for loop with DispatchQueue.main.async and it didn't make a difference. In short - I would like to run both for loops at the same time (or perceived as such). These are on the UI thread.
I tried this and it seemed to work out quite well. It does exactly what I want it to do (at least visually they seem to run at the same time).
let concurrentQueue = DispatchQueue(label: "net.ericd.hello", attributes: .concurrent)
concurrentQueue.async {
//my loop with delay here for object 1.
}
concurrentQueue.async {
//my separate loop with delay here for object 2.
}
We can use it when we want execute different arrays at the same time:
using this Generic Function
zip(_:_:)
Here i took 2 array:
var arrOfInt = ["1","2","3"]
var arrOfIntString = ["one","two","three"]
for (intNum, intString) in zip(arrOfInt, arrOfIntString) {
print("Int:\(intNum), String:\(intString)")
}

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.

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

Why is runAction() function being called multiple times within my update() method?

Within my func update(currentTime: CFTimeInterval) method in Sprite Kit, I have a gameTicker that increments by one integer every time the game is updated.
When the gameTicker is divisible by 500, I pause the ticker, disable enemies from spawning by removing the original action called in didMoveToView(), and start a nextLevelDelayTicker that functions as a brief delay. Once the nextLevelDelayTicker reaches 100, I start incrementing the original gameTicker again, reset the nextLevelDelayTicker to 0, and run an action to start spawning enemies again.
Basically, once the nextLevelDelayTicker is equal to 100, I only want to run the contents of the conditional one time. I added a print("yolo") in there to see if it was only being called once when the conditional is met, and indeed it is. However, for some reason runAction(spawnAction, withKey: "spawnAction") is being called multiple times. As soon as the condition self.nextLevelDelayTicker.getTimePassed() == 100 is met, an enormous amount of enemies are being spawned within a very short period of time.
How is runAction(spawnAction, withKey: "spawnAction") being called multiple times, but print("yolo") is only being called once?
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if gameTicker.isActive == true {
gameTicker.increment()
}
// If gameTicker is equal to 5 seconds, increase enemy amount
if gameTicker.getTimePassed() % 500 == 0 {
self.enemyAmount += 1
self.removeActionForKey("spawnAction")
gameTicker.isActive = false
}
// If level has been completed and last ghost has been killed, activate next level scene
if gameTicker.isActive == false && enemyArray.count == 0 {
self.nextLevelDelayTicker.increment()
if self.nextLevelDelayTicker.getTimePassed() == 100 {
print("YOLO")
self.gameTicker.isActive = true
self.nextLevelDelayTicker.reset()
let spawnAction = SKAction.repeatActionForever(
SKAction.sequence([
SKAction.waitForDuration(2),
SKAction.runBlock({
[unowned self] in
self.spawnEnemy()
})
])
)
runAction(spawnAction, withKey: "spawnAction")
}
}
}
So it turns out I'm a dodo head. runAction(spawnAction, withKey: "spawnAction") wasn't being called more than once, it was:
if gameTicker.getTimePassed() % 500 == 0 {
self.enemyAmount += 1
self.removeActionForKey("spawnAction")
gameTicker.isActive = false
}
being called multiple times, adding an enormous amount of enemies to the self.enemyAmount global variable that was being used to determine how many enemies spawn when spawnAction was run.
I added a flag to the conditional and it is only being called once every level cycle compared to what was ~300 times per game cycle:
if gameTicker.getTimePassed() % 500 == 0 && gameTicker.isActive == true{
self.enemyAmount += 1
self.removeActionForKey("spawnAction")
gameTicker.isActive = false
print("STOP SPAWNING")
}

Swift - SpriteKit - Update SKAction.waitForDuration while running forever

I am trying to update the waitForDuration action in a sequence that is running forever.
override func didMoveToView(view: SKView) {
//code
runAction(SKAction.repeatActionForever(runSeq()))
}
func runSeq() -> SKAction{
var difficulty: CGFloat = 0.75
let updateAction = SKAction.runBlock({
self.runCount++
self.runCount %= 4
println(self.runCount)
if self.runCount == 0 {
difficulty -= 0.1
}
if self.children.count > 51{
println("You loose")
}else{
self.scoreLabel.text = String(format: "Score: %i", self.score)
}
})
let createAntAction = SKAction.runBlock({self.createAnt()})
var wait = SKAction.waitForDuration(NSTimeInterval(difficulty))
let seq = SKAction.sequence([createAntAction, wait, updateAction])
return seq
}
difficulty is the value that I want, and every 4 loops I want it to decrease by a certain amount. Currently, the starting value remains the same, and doesn't change, even though I am changing the difficulty value, and recalling the sequence of actions every time.
This is happening because wait is already established as .75, and nothing is changing that. SKAction.waitForDuration(NSTimeInterval(difficulty)) is not looking for a reference to difficulty, only a value, so you need to rethink how you want to code this.