多个 SKSpriteNode 的 SpriteKit 多个 SKActions - 等待所有完成
SpriteKit multiple SKActions for multiple SKSpriteNode - wait for all to complete
我正在开发 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
如何让我的 TODO 代码在所有 SKActions 之后成为执行者?
我想 运行 并行或一个接一个地执行 SKAction。
谢谢。
您可以使用带有 运行 方法的完成块非常轻松地完成此操作。
为了这个例子,假设您有一个名为 someSpriteNode 的 SKSpriteNode
并且想知道两个动作(在这种情况下为 applyImpulse)何时完成 运行ning:
// 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
})
我认为这个解决方案比计数器更优雅 :)
First of all you should always create a Minimum Verifiable Example. Remove the unneeded things from your question and make sure to include the everything we needed to test your code.
前提
我假设你有一个 Tile
class 类似于此
class Tile {
var sprite: SKSpriteNode?
}
和这样的数组
let tiles:[Tile] = ...
你的objective
- 您想 运行 对图块中每个图块的 sprite 元素执行随机持续时间的操作。
- 您希望操作同时开始
- 您希望能够在所有操作完成后运行一些代码
解决方案
// 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")
}
我正在开发 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
如何让我的 TODO 代码在所有 SKActions 之后成为执行者? 我想 运行 并行或一个接一个地执行 SKAction。
谢谢。
您可以使用带有 运行 方法的完成块非常轻松地完成此操作。
为了这个例子,假设您有一个名为 someSpriteNode 的 SKSpriteNode
并且想知道两个动作(在这种情况下为 applyImpulse)何时完成 运行ning:
// 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
})
我认为这个解决方案比计数器更优雅 :)
First of all you should always create a Minimum Verifiable Example. Remove the unneeded things from your question and make sure to include the everything we needed to test your code.
前提
我假设你有一个 Tile
class 类似于此
class Tile {
var sprite: SKSpriteNode?
}
和这样的数组
let tiles:[Tile] = ...
你的objective
- 您想 运行 对图块中每个图块的 sprite 元素执行随机持续时间的操作。
- 您希望操作同时开始
- 您希望能够在所有操作完成后运行一些代码
解决方案
// 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")
}