Error with Dispatch Queue Swift - swift

I am trying to create a genetic algorithm for running race cars around a race track. Each car gets random instructions that apply a force to the car and rotate the car by a certain number of degrees. In order to space out the new instructions given to each car, I used a delay time in dispatch Queue that adds 0.2 seconds to the previous instruction.
e.g.
0 seconds - first instruction
0.2 seconds - second instruction
0.4 seconds -third instruction
and so on...
The problem I have is that after several instructions have been carried out I start to notice a longer delay between instructions, to the point where a new instruction is applied after say 2 seconds.
Here is my code below.
func carAction(newCar: [[CGFloat]], racecar: SKSpriteNode) {
var caralive = true
let max = 1000
var count = 0
let delayTime = 200000000
var deadlineTime = DispatchTime.now()
while count < max {
let angleChange = newCar[count][1]
let speedChange = newCar[count][0]
count += 1
deadlineTime = deadlineTime + .nanoseconds(delayTime)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
if caralive == true {
print(DispatchQueue.main)
racecar.physicsBody?.angularVelocity = 0
let rotate = SKAction.rotate(byAngle: (angleChange * .pi / 180), duration: 0.2)
racecar.run(rotate)
let racecarRotation : CGFloat = racecar.zRotation
var calcRotation : Float = Float(racecarRotation) + Float(M_PI_2)
let Vx = speedChange * CGFloat(cosf(calcRotation))
let Vy = speedChange * CGFloat(sinf(calcRotation))
let force = SKAction.applyForce(CGVector(dx: Vx, dy: Vy), duration: 0.2)
racecar.run(force)
let total = self.outerTrack.count
var initial = 0
while initial < total {
if racecar.intersects(self.outerTrack[initial]) {
racecar.removeFromParent()
self.numberOfCars -= 1
initial += 1
caralive = false
break
} else {
initial += 1
}
}
} else {
// print(self.numberOfCars)
}
}
}
The 2D array newCar is a list of all the instructions.
Any help would be massively appreciated as I have been trying to figure this out for ages now!!
Many thanks in advance, any questions just feel free to ask!

You should do something like this instead:
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function "updateCounting" with the interval of 1 seconds
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("moveCarsFunction"), userInfo: nil, repeats: true)
}
And call scheduledTimerWithInterval once
originally answered here

Related

Step Counter Sprite Kit

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()
}

Swift Timer that will not run again until a certain period of time

This is proving to be a challenge, I want a timer that triggers a pulse for a 10th of a second, then effectively blocks it from running again until at least 1 second has past.
This is the code for the pulse , but how can I block it from triggering again until 1 second has passed - could I put a timer within a timer?
Any help would be greatly appreciated.
func pulseOn(on: Bool) {
var pulseCount = 4
Timer.scheduledTimer(withTimeInterval: 0.025, repeats: true)
{ timer in
print("PulseCount: \(pulseCount)")
pulseCount = pulseCount - 1
if pulseCount < 1 {
timer.invalidate()
}
}
}
When measuring time between two events, use Date and its timeIntervalSince* functions instead of a timer.
Use a property lastPulse to record the time of the last pulse and have a Bool property pulseActive that keeps track if a pulse in in progress. Only run your function if 1 second has passed and a pulse is not active.
// time of last pulse
// seed lastPulse with distantPast so first pulse will always succeed
var lastPulse = Date.distantPast
// are we in the middle of running a pulse?
var pulseActive = false
func pulseOn(on: Bool) {
guard !pulseActive && abs(lastPulse.timeIntervalSinceNow) >= 1 else { return }
pulseActive = true
// record the time of this pulse
lastPulse = Date()
var pulseCount = 4
Timer.scheduledTimer(withTimeInterval: 0.025, repeats: true)
{ timer in
print("PulseCount: \(pulseCount)")
pulseCount = pulseCount - 1
if pulseCount < 1 {
timer.invalidate()
pulseActive = false
// If you want the time from last pulse end to be 1 second
// then set lastPulse here instead
//lastPulse = Date()
}
}
}
Notes:
Your on: Bool is unused. I have left it there because I wanted to highlight just the new code.
It wasn't clear if you wanted the 1 second interval to be from pulse start to pulse start or pulse end to pulse start. If you want the pulse starts to be 1 second apart at least, then use the code as written. If you want the next pulse to start at least one second after the previous pulse ended, then set lastPulse = Date() when the timer is invalidated.
May something like this. add a bool;
var runPulse: Bool = false
orginal timer function:
func pulseOn(on: Bool) {
var pulseCount = 4
Timer.scheduledTimer(withTimeInterval: 0.025, repeats: true)
{ timer in
print("PulseCount: \(pulseCount)")
secondCheck() //new funtion
pulseCount = pulseCount - 1
if pulseCount < 1 {
timer.invalidate()
}
}
}
New function:
func pulseOn(on: Bool) {
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false)
{ timer in
self.runPulse = true
}
}
Then wherever you trigger the pulseOn func you can add an if statement.
if runPulse {
pulseOn()
}

calling function from an array with time delay

Let's say I have this array of functions:
lazy var funcArray = [firstFunc, secondFunc, thirdFunc, ....n+Func, ...Inifinit number of Func........]
How would you call them one after the other with a 2.5 second delay?
I have tried without success many things including this while loop:
while n < funcArray.count {
funcArray[n]()
DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
n = n +1
}
}
i write small code for better understanding.
initialize count and funcTimer variable and created static function array .
var count:Int = 0
var funcTimer = Timer()
let funcArray:Array = [somefunc(),somefunc(),somefunc()]
After that add these line in appropriate place
funcTimer = Timer.scheduledTimer(timeInterval: 2.5, target: self, selector: (#selector(ViewController.scheduleArrayFunctions)), userInfo: nil, repeats: true)
#objc func scheduleArrayFunctions(funcarray:[ String])
{
if count < self.funcArray.count {
//this if is interesting checking both static function when it check it call the somefunc method
if self.funcArray[count] == ViewController.somefunc(){
print("equel")
}
self.count += 1
}
else
{
funcTimer.invalidate()
}
}
func somefunc()
{
print("hello world")
}
Hope so this will work for you.
I think this will work. Follow these steps.
Declare a variable count as zero.
Schedule a timer with time interval 2.5 and repeat to true.
Now call the function from the array with index as count inside the timer callback block.
Check if the count is less than array.count.
Increment the count.
Otherwise stop the timer.
I did a function to function loop
probably bad coding but...it works
n = 0
self.funcArray[n]()
n = n + 1
timerAction()
func timerAction() {
let when = DispatchTime.now() + 2.5
DispatchQueue.main.asyncAfter(deadline: when) {
self.funcArray[self.n]()
self.n = self.n + 1
if self.n < self.funcArray.count {
self.timerAction2()
}
}
}
func timerAction2() {
let when = DispatchTime.now() + 2.5
DispatchQueue.main.asyncAfter(deadline: when) {
self.funcArray[self.n]()
}
if self.n < self.funcArray.count {
self.timerAction()
}
}

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.

Waiting for SKAction completion inside a for loop

I have a for loop that runs 10 times, each time creating a node and adding it to the scene. However, I want there to be a delay in between each node being added (add a node, wait a second, add a node, wait a second, etc.)
However, after the first 1 second, all 10 nodes are added at the same time. How can I achieve this desired effect of waiting a second in between each node being added?
Here is my code:
EDIT:
func createText(correct: Bool) {
let text = SKNode()
var line: String!
addChild(text)
if correct {
line = (GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(happyLines) as! [String])[0]
} else {
line = (GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(sadLines) as! [String])[0]
}
var xPos = self.frame.midX + 300
var yPos = self.frame.midY
var spaces = 1
// For each character in sentence, create a node of it
for character in line.characters {
runAction(SKAction.waitForDuration(1.0)) {
if spaces == 4 {
spaces = 0
print("space")
xPos = self.frame.midX + 300
yPos -= 30
}
xPos += 10
if character != " " {
let letter = SKLabelNode(fontNamed: GameScene.fontName)
letter.fontSize = 14 * GameScene.fontScale
letter.position = CGPoint(x: xPos, y: yPos)
letter.text = String(character)
text.addChild(letter)
} else {
spaces += 1
xPos += 10
}
}
}
runAction(SKAction.waitForDuration(2.0)) {
text.removeAllChildren()
text.removeFromParent()
}
}
You can achieve this using actions.
First, create actions for the delay and adding the node to the scene.
let waitAction = SKAction.wait(forDuration: 1)
let addNodeAction = SKAction.run {
let node = SKSpriteNode(imageNamed: "example")
self.addChild(node)
}
Next, create a sequence of actions so that the delay always occurs before the node is added to the scene.
let sequenceAction = SKAction.sequence([waitAction, addNodeAction])
Next, create an action that repeats the sequence 10 times.
let repeatAction = SKAction.repeat(sequenceAction, count: 10)
Finally, run this action and watch the nodes appear!
run(repeatAction)
EDIT:
To solve your second question in the comments about needing access to the current character (making each action where the node is added slightly different), loop through your characters to build a sequence of actions, and then run that action.
var actions = [SKAction]()
let waitAction = SKAction.wait(forDuration: 1)
for character in line.characters {
let addNodeAction = SKAction.run {
let node = SKSpriteNode(imageNamed: "example")
// use the character variable here
self.addChild(node)
}
actions.append(waitAction)
actions.append(addNodeAction)
}
let sequenceAction = SKAction.sequence(actions)
run(sequenceAction)
How about this? (not tested :)
for (i, character) in line.characters.enumerate() {
runAction(SKAction.waitForDuration(Double(i) * 1.0)) {
// rest of your for loop
So you enumerate through the characters, where i is the i-th character, starting at 0. With each character, you just multiply its index position in the string with the 1 second interval to give you the wait duration.