SpriteKit用于多个SKSpriteNode的多个SKActions - 等待所有人完成

时间:2017-09-23 08:08:12

标签: swift sprite-kit skaction

我正在进行iOs游戏,但我遇到了问题。 我需要显示x精灵(每个我都有一个刻度SKAction)。 我需要等到所有SKAction精灵再做其他事情。 每个SKAction都在一个单独的线程中运行。 我怎么等?

这是一段代码:

for tile in tiles {

            let randomNum:UInt32 = arc4random_uniform(20) // range is 0 to 99
            let randomTime:TimeInterval = TimeInterval(randomNum/10)
            let scale = SKAction.scale(by: 1, duration: 2, delay:randomTime , usingSpringWithDamping: 0.1, initialSpringVelocity: 5)

            tile.sprite = SKSpriteNode(imageNamed: tile.type.spriteName)
            tile.sprite?.size = CGSize(width: TileWidth, height: TileHeight)
            tile.sprite?.position = tile.position!

            tile.sprite?.scale(to: CGSize(width: 0, height: 0))
            cookiesLayer.addChild(tile.sprite!)
            tile.sprite?.run(scale)


        }
//TODO code to add to be executed after all SKActions

如何在所有SKActions之后将我的TODO代码作为执行者? 我想并行或一个接一个地运行SKAction。

感谢。

2 个答案:

答案 0 :(得分:4)

您可以使用run方法使用完成块轻松完成此操作。

只是为了这个例子,假设你有一个名为someSpriteNode的SKSpriteNode,并想知道两个动作(在这种情况下是applyImpulse)何时完成运行:

    // 1) Create your actions:

    let action1 = SKAction.applyImpulse(CGVector(dx: 1.0, dy: 0.0), duration: 2.0)
    let action2 = SKAction.applyImpulse(CGVector(dx: 6.0, dy: 2.0), duration: 1.0)

     // 2) Add them to a sequence:

    let actionSequence = SKAction.sequence([action1, action2])

     // 3) Run the sequence using a completion block:

    someSpriteNode?.run(actionSequence, completion: {

         // All your actions are now finished

         // Do whatever you want here :)
    })

更新:在执行一组操作时获得通知,其中所有操作都在同一节点上运行

您可能正在寻找行动小组:

// Declare an empty array that will store all your actions:

var actions = [SKAction]()

// Iterate through your nodes:

for _ in 0..<6 {

    // ...

    // Generate your random scale, delay, or whatever you need:

    let randomScale = CGFloat(GKRandomDistribution(lowestValue: 0, highestValue: 10).nextInt())

    // Create your custom action

    let scaleAction = SKAction.scale(by: randomScale, duration: 2.0)

    // Append your action to the actions array:

    actions.append(scaleAction)
}


// Create an action group using the actions array:

let actionGroup = SKAction.group(actions)

// Run your action group, and do whatever you need inside the completion block:

self.run(actionGroup, completion: {

    // All your actions are now finished, no matter what node they were ran on.
})

此外,我建议您使用GameplayKit在游戏中生成随机数字,这肯定会让您的生活更轻松:)

更新2:在所有操作都执行后获得通知,如果所有操作都在不同节点上运行

使用DispatchGroup

    // Create a DispatchGroup:

    let dispatchGroup = DispatchGroup()

    for _ in 0..<6 {

        // ...

        let randomWait = Double(GKRandomDistribution(lowestValue: 1, highestValue: 12).nextInt())

        let waitAction = SKAction.wait(forDuration: randomWait)
        let fadeOutAction = SKAction.fadeOut(withDuration: 2.0)
        let fadeInAction = SKAction.fadeIn(withDuration: 2.0)
        let sequenceAction = SKAction.sequence([waitAction, fadeOutAction, fadeInAction])

        // Enter the DispatchGroup

        dispatchGroup.enter()

        colorSquares[i].run(sequenceAction, completion: {

             // Leave the DispatchGroup

             dispatchGroup.leave()
        })

    }

   // Get notified when all your actions left the DispatchGroup:

    dispatchGroup.notify(queue: DispatchQueue.main, execute: {

        // When this block is executed, all your actions are now finished
    })

我认为这种解决方案比计数器更优雅:)

答案 1 :(得分:1)

  

首先,您应该始终创建Minimum Verifiable Example。从您的问题中删除不需要的东西,并确保包含测试代码所需的一切。

前提

我假设您有一个与此类似的Tile

class Tile {
    var sprite: SKSpriteNode?
}

和这样的数组

let tiles:[Tile] = ...

您的目标

  1. 您希望在tile中每个tile的sprite元素上运行随机持续时间的操作。
  2. 您希望操作同时启动
  3. 您希望能够在完成所有操作后运行一些代码
  4. 解决方案

    // 0. create a maxDuration variable
    var maxDuration:TimeInterval = 0
    
    // 1. create all the actions
    let actions = tiles.map { tile in
        return SKAction.run {
            let randomNum = arc4random_uniform(100)
            let randomTime = TimeInterval(randomNum / 10)
            let wait = SKAction.wait(forDuration: randomTime)
            let scale = SKAction.scale(by: 1, duration: 2)
            tile.sprite?.run(scale)
            maxDuration = max(maxDuration, randomTime + 2)
        }
    }
    
    // 2. create a wait action for the max duration
    let wait = SKAction.wait(forDuration: maxDuration)
    
    // 3. write inside this action the code to be executed after all the actions
    let completion = SKAction.run {
        print("now all the actions are completed")
    }
    // 4. create a sequence of wait + completion
    let sequence = SKAction.sequence([wait, completion])
    
    // 5. create a group to run in parallel actions + sequence
    let group = SKAction.group(actions + [sequence])
    
    // 6. run the group on the node you prefer (it doesn't really matter which node since every inner action is tied to a specific node)
    self.run(group)
    

    更新(由@Alex建议)

    var maxDuration:TimeInterval = 0
    
    tiles.forEach { tile in
        let randomNum = arc4random_uniform(100)
        let randomTime = TimeInterval(randomNum / 10)
        let wait = SKAction.wait(forDuration: randomTime)
        let scale = SKAction.scale(by: 1, duration: 2)
        tile.sprite?.run(scale)
        maxDuration = max(maxDuration, randomTime + 2)
    }
    
    run(.wait(forDuration: maxDuration)) {
        print("now all the actions are completed")
    }