简体   繁体   中英

Swift spawn nodes infinietly

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM