How can I create a loop that runs two functions, never overlapping? - swift

I'm new to programming and have been taking online courses in swift and spritekit trying to create my first working game from scratch.
Currently I'm having an issue trying to create a sequence of functions that run independently, wait so that only one is running at a time, and loops indefinitely.
The first function:
func shootTwentyArrows() {
var oneArrow = SKAction.runBlock{
self.shootArrow()
}
var waitBetweenArrows = SKAction.waitForDuration(arrowSpeed)
var fireAnArrow = SKAction.sequence([oneArrow, waitBetweenArrows])
self.runAction(SKAction.repeatAction(fireAnArrow, count: 20))
}
And the second function:
func shootSpiral1() {
var leftArrow = SKAction.runBlock{
self.arrowFromLeft()
}
var rightArrow = SKAction.runBlock{
self.arrowFromRight()
}
var waitBetweenArrows = SKAction.waitForDuration(arrowSpeed)
var sequence = SKAction.sequence([leftArrow, waitBetweenArrows, rightArrow, waitBetweenArrows])
var spiral1 = SKAction.repeatAction(sequence, count: 5)
self.runAction(spiral1)
to clarify, I'm trying to run something like:
shootTwentyArrows()
when that's done, shootSpiral1(), when that's done repeat.
Thanks in advance for any responses.

I guess the most correct way to do that would be to refactor code a little bit:
func shootTwentyArrows() -> SKAction {
let oneArrow = SKAction.runBlock{
self.shootArrow()
}
let waitBetweenArrows = SKAction.waitForDuration(arrowSpeed)
let fireAnArrow = SKAction.sequence([oneArrow, waitBetweenArrows])
return SKAction.repeatAction(fireAnArrow, count: 20)
}
func shootSpiral1() -> SKAction {
let leftArrow = SKAction.runBlock{
self.arrowFromLeft()
}
let rightArrow = SKAction.runBlock{
self.arrowFromRight()
}
let waitBetweenArrows = SKAction.waitForDuration(arrowSpeed)
let sequence = SKAction.sequence([leftArrow, waitBetweenArrows, rightArrow, waitBetweenArrows])
let spiral1 = SKAction.repeatAction(sequence, count: 5)
return spiral1
}
Then somewhere in the code you can just do something like that :
let spiralAction = shootSpiral1()
let oneArrowAction = shootTwentyArrows()
let sequence = SKAction.sequence([spiralAction, oneArrowAction])
let infiniteSequence = SKAction.repeatActionForever(sequence)
self.runAction(infiniteSequence)
I left function names the same on purpose, so you get the idea.
P.S. It is a common practice to declare a variable as let, declare it as var only when you have to modify it later.

The best way to do this is have an SKAction sequence that calls both of your functions. You would call this SKAction from wherever you want to initialize the arrow shooting actions like viewDidLoad for example. This would be the code to call the actions...
var actionShootingArrows = SKAction.sequence([shootSpiral1(), shootTwentyArrows()])
self.runAction(SKAction.repeatActionForever(actionShootingArrows))
Hope this helps!

Related

"Attemped to add a SKNode which already has a parent:" in Repeat Loop. Any simple work around?

I am pretty Newbie to programming. And I am trying to pile up the random blocks dynamically till it hits the upper frame. But it seems that Swift doesn't let me to do so. Did I miss anything please? Any input are appreciated.
let blocks =[block1,block2,block3,block4,block5,block6,block7,block8,block9,block10,block11,block12]
var block:SKSpriteNode!
let blockX:Double = 0.0
var blockY:Double = -(self.size.height/2)
repeat{
block = blocks.randomBlock()
block.zPosition = 2
block.position = CGPoint(x:blockX, y:blockY)
block.size.height = 50
block.size.width = 50
self.addChild(block)
blockY += 50
} while( block.position.y < self.size.height)
extension Array {
func randomBlock()-> Element {
let randint = Int(arc4random_uniform(UInt32(self.count)))
return self[randint]
}
}
you need to have someway of tracking which blocks have been selected and ensure that they don't get selected again. The method below uses an array to store the indexes of selected blocks and then uses recursion to find a cycle through until an unused match is found.
private var usedBlocks = [Int]()
func randomBlock() -> Int {
guard usedBlocks.count != blocks.count else { return -1 }
let random = Int(arc4random_uniform(UInt32(blocks.count)))
if usedBlocks.contains(random) {
return randomBlock()
}
usedBlocks.append(random)
return random
}
in your loop change your initializer to
let index = randomBlock()
if index > -1 {
block = blocks[index]
block.zPosition = 2
block.position = CGPoint(x:blockX, y:blockY)
}
remember that if you restart the game or start a new level, etc. you must clear all of the objects from usedBlocks
usedBlocks.removeAll()

Is there a way to override the Copy on Write behavior for Swift arrays?

I'm working on a project where I need to work with large arrays, and by using UnsafeMutablePointers, I get a threefold speed increase over using the regular array methods. However, I believe the copy on write behavior is causing me to change instances that I do not want to be affected. For example, in the following code, I want to update the values in copyArray, but leave the original values in anArray.
import Foundation
func increaseWithPointers(_ arr: inout [Int]) {
let count = arr.count
let ptr = UnsafeMutablePointer(mutating: &arr)
for i in 0..<count {
ptr[i] = ptr[i] + 1
}
}
var anArray = [1,2,3,4,5]
var copyArray = anArray
increaseWithPointers(&copyArray)
print(anArray)
Executing this code prints [2,3,4,5,6].
I can get around this by declaring copyArray as follows:
var copyArray = [Int](repeating: 0, count: 5)
for i in 0..<5 {
copyArray[i] = anArray[i]
}
However, this requires writing each value twice: to zero, then to the intended value. Is there a way to efficiently guarantee a copy of an array?
I can reproduce your problem using Xcode 9 beta 3, but not using Xcode 8.3.3. I suggest you file a Swift bug report.
This fixes the problem:
import Foundation
func increaseWithPointers(_ arr: inout [Int]) {
arr.withUnsafeMutableBufferPointer { (buffer) in
for i in buffer.indices {
buffer[i] += 1
}
}
}
var anArray = [1,2,3,4,5]
var copyArray = anArray
increaseWithPointers(&copyArray)
print(anArray)

Running functions in sequence

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.

Objects in Swift: Value of 'Object' has no member

Here's my doozy.
I've got this lovely little function in a file called functions.swift
//functions.swift
func latestActiveGoal() -> Object {
let realm = try! Realm()
let currentGoal = realm.objects(Goal).filter("Active == 1").sorted("CreatedOn").last
return currentGoal!
}
which returns a Goal object. (A Goal might be wanting to lose weight, or stop being so inept at Swift).
In a different view controller, I want to access this object. Here's what I'm trying:
//viewController.swift
#IBOutlet weak var aimText: UILabel!
let funky = functions()
func getGoals(){
var currentGoal = funky.latestActiveGoal()
print(currentGoal)
aimText.text = currentGoal.Title
}
The print(CurrentGoal) output shows this:
Goal {
id = 276;
Title = Goal Title;
Aim = Aim;
Action = Nothing;
Active = 1;
CreatedOn = 2016-02-12 00:14:45 +0000;
}
aimText.text = currentGoal.Title and aimText = currentGoal.Title both throw the error:
Value of 'Object' has no member 'Title'
By printing the contents of the object, I can see the data, but can't figure out how. Any help greatly appreciated.
As the error message said, currentGoal is a value of Object type which doesn't have member Title.
This is because function latestActiveGoal returns Object instead of Goal. You just need to make it return Goal by change the return type:
func latestActiveGoal() -> Goal {
Just replace your functions with below code.
It will works perfect.
This fuction will check if goal available, then only it will return.
func latestActiveGoal() -> Object? {
let realm = try! Realm()
let currentGoals = realm.objects(Goal).filter("Active == 1").sorted("CreatedOn")
if currentGoals.count > 0 {
return currentGoals.last;
}
return nil;
}
Your getGoals method will be as follow.
func getGoals(){
if let currentGoalObject = funky.latestActiveGoal() {
print(currentGoalObject)
let goal = currentGoalObject as! Goal
print(goal.Title)
aimText.text = goal.Title
}
}

Strange values from vDSP_meanD

I am using the vDSP_meanD function to determine the average of a data set (consecutive diferences from an array)
The code I am using is below
func F(dataAllFrames:[Double],std:Double,medida:String)->Double{
let nframes=dataAllFrames.count
var diferencas_consecutivas_media = [Double](count: dataAllFrames.count-1, repeatedValue:0.0)
var mediaDifConseq:Double = 0
for(var i:Int=1; i<dataAllFrames.count; i++){
diferencas_consecutivas_media[i-1]=dataAllFrames[i]-dataAllFrames[i-1]
}
var meanConseqDif = [Double](count: 1, repeatedValue:0.0)
var meanConseqDifPtr = UnsafeMutablePointer<Double>(meanConseqDif)
vDSP_meanvD(diferencas_consecutivas_media,1,meanConseqDifPtr,UInt(nframes))
print( meanConseqDif[0])
}
The function F is called within a thread block
let group = dispatch_group_create()
let queue = dispatch_queue_create("myqueue.data.processor", DISPATCH_QUEUE_CONCURRENT)
dispatch_group_async(group, queue) {
F(measureData,std: std, medida: medida)
}
The F function is called in multiple dispatch block with different variables instances every now and then i get different values for the value returned from vDSP_meanD is there any context where this may happen ?
May the thread call have some influence on that?
Any "lights" would be greatly appreciated
I wouldn't expect this code to work. This shouldn't be correct:
var meanConseqDif = [Double](count: 1, repeatedValue:0.0)
var meanConseqDifPtr = UnsafeMutablePointer<Double>(meanConseqDif)
vDSP_meanvD(diferencas_consecutivas_media,1,meanConseqDifPtr,UInt(nframes))
I believe this is pointing directly at the Array struct, so you're probably blowing away the metadata rather than updating the value you meant. But I would expect that you don't get the right answers at all in that case. Have you validated that your results are correct usually?
I think the code you mean is like this:
func F(dataAllFrames: [Double], std: Double, medida: String) -> Double {
let nframes = UInt(dataAllFrames.count)
var diferencas_consecutivas_media = [Double](count: dataAllFrames.count-1, repeatedValue:0.0)
for(var i = 1; i < dataAllFrames.count; i += 1) {
diferencas_consecutivas_media[i-1] = dataAllFrames[i] - dataAllFrames[i-1]
}
var mediaDifConseq = 0.0
vDSP_meanvD(diferencas_consecutivas_media, 1, &mediaDifConseq, nframes)
return mediaDifConseq
}
You don't need an output array to collect a single result. You can just use a Double directly, and use & to take an unsafe pointer to it.
Unrelated point, but you can get rid of all of the difference-generating code with a single zip and map:
let diferencasConsecutivasMedia = zip(dataAllFrames, dataAllFrames.dropFirst())
.map { $1 - $0 }
I haven't profiled these two approaches, though. It's possible that your approach is faster. I find the zip and map much clearer and less error-prone, but others may feel differently.