I am trying to get my nodes to move across the screen and spawn infinitely on a timer. I would aslo like to have multiple nodes on screen at once. This is my stable code so far. I have tried multiple different ways with either freezes or crashes.
let bunnyTexture = SKTexture(imageNamed: "oval 1.png")
bunny = SKSpriteNode(texture: bunnyTexture)
bunny.position = CGPoint(x: (size.width / 3), y: 750 + bunny.frame.height)
self.addChild(bunny)
bunny.physicsBody = SKPhysicsBody(circleOfRadius: bunnyTexture.size().height/2)
bunny.physicsBody!.dynamic = true
let spawnBunny = SKAction.moveByX(0, y: -self.frame.size.height, duration: 4)
let despawnBunny = SKAction.removeFromParent()
let spawnNdespawn2 = SKAction.sequence([spawnBunny, despawnBunny])
bunny.runAction(spawnNdespawn2)
let gobblinTexture = SKTexture(imageNamed: "oval 2.png")
gobblin = SKSpriteNode(texture: gobblinTexture)
gobblin.position = CGPoint(x: 150 + gobblin.frame.width, y: (size.height / 3))
self.addChild(gobblin)
let randomGob = arc4random() % UInt32(self.frame.size.height / 2)
let spawnGob = SKAction.moveByX(+self.frame.size.width, y: 0, duration: 4)
let despawnGob = SKAction.removeFromParent()
let spawnNdespawn = SKAction.sequence([spawnGob, despawnGob])
gobblin.runAction(spawnNdespawn)
let ground = SKNode()
ground.position = CGPointMake(0, 0)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, -50))
ground.physicsBody!.dynamic = false
self.addChild(ground)
You can use SKAction.waitForDuration(interval)
to create a looping timer.
Extend SKNode
with a new function loopAction
. This takes a SKAction
an NSTimeInterval
and a () -> Bool
function.
The SKAction
is the function that will be executed at each interval. The () -> Bool
function will be used to stop the otherwise infinite loop.
Just remember that this captures both the SKNode
and the SKAction
. Neither will deallocate until the loop is stopped.
Its possible to create an object with a flag ( Bool
) to hold all this information and set the flag to false when it needs to stop. I just prefer this way.
LoopActionManager
is an example of how the loopAction
is implemented. You need an SKNode
and an SKAction
. Instead of calling runAction
on the SKNode
you call loopAction
and pass the interval and the function that controls when to stop. You would place this code somewhere you have access to the nodes in question.
The function to stop can also be implemented as a trailing closure
Paste the code into a playground to see how it works.
import UIKit
import SpriteKit
import XCPlayground
extension SKNode {
func loopAction(action:SKAction,interval:NSTimeInterval,continueLoop:() -> Bool) {
guard continueLoop() else { return }
runAction(SKAction.waitForDuration(interval)) {
if continueLoop() {
self.runAction(action)
self.loopAction(action, interval: interval, continueLoop: continueLoop)
}
}
}
}
// example
class LoopActionManager {
let node = SKSpriteNode(color: UIColor.whiteColor(), size: CGSize(width: 20, height: 20))
let action = SKAction.moveBy(CGVector(dx: 10,dy: 0), duration: 0.5)
func continueMoveLoop() -> Bool {
return (node.position.x + node.size.width) < node.scene?.size.width
}
func start() {
node.loopAction(action, interval: 1, continueLoop: continueMoveLoop)
}
}
let view = SKView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
let scene = SKScene(size: view.frame.size)
view.presentScene(scene)
let example = LoopActionManager()
scene.addChild(example.node)
example.start()
XCPlaygroundPage.currentPage.liveView = view
As stated in the comments below it is also possible to use repeatAction
. You can extend SKAction
with a static func
to generate the repeatAction
based on an action to repeat and an interval. In comparison to the first solution this is simpler and more in line with the SDK. You do lose the completion handler for each interval.
import UIKit
import SpriteKit
import XCPlayground
extension SKAction {
static func repeatAction(action:SKAction,interval:NSTimeInterval) -> SKAction {
// diff between interval and action.duration will be the wait time. This makes interval the interval between action starts. Max of 0 and diff to make sure it isn't smaller than 0
let waitAction = SKAction.waitForDuration(max(0,interval - action.duration))
let sequenceAction = SKAction.sequence([waitAction,action])
let repeatAction = SKAction.repeatActionForever(sequenceAction)
return repeatAction
}
}
let view = SKView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
let scene = SKScene(size: view.frame.size)
view.presentScene(scene)
let node = SKSpriteNode(color: UIColor.whiteColor(), size: CGSize(width: 20, height: 20))
let action = SKAction.moveBy(CGVector(dx: 10,dy: 0), duration: 0.5)
scene.addChild(node)
node.runAction(SKAction.repeatAction(action, interval: 1.0))
XCPlaygroundPage.currentPage.liveView = view
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.