问题
I understand quite well the SKAction API but I cannot achieve to have good code when running sequential code on multiple nodes.
Here is a sample code (simplified) :
import SpriteKit
class GameScene: SKScene {
weak var node1: SKNode!
weak var node2: SKNode!
override func didMove(to view: SKView) {
super.didMove(to: view)
cacheUI()
runSampleSequence()
}
private func cacheUI() {
// Fetch nodes from associated SKS file
node1 = self.childNode(withName: "node1")!
node2 = self.childNode(withName: "node2")!
}
private func runSampleSequence() {
// I want node1 to fadeIn
// then node2 to fadeIn
// then node1 to fadeOut
// then node2 to fadeOut
node1.run(SKAction.sequence([
SKAction.fadeIn(withDuration: 1),
SKaction.run { [weak self] in
self.node2.run(SKAction.fadeIn(withDuration: 2))
},
SKAction.fadeOut(withDuration: 4),
SKaction.run { [weak self] in
self.node2.run(SKAction.fadeOut(withDuration: 3))
}
]))
}
}
The previous code will run perfectly, but will result with boilerplate code if we add node3, node4 ... I would like to be able to refactor it to be easier to read and maintain given specifications. Something like :
// Does not compile
self.run(SKAction.sequence([
node1.fadeIn(withDuration: 1),
node2.fadeIn(withDuration: 2),
node1.fadeOut(withDuration: 4),
node2.fadeOut(withDuration: 3)
]))
Can I achieve this ? I want the business code to appear as clear as possible.
How does SKAction.sequence(_:) behave exactly when using SKAction.run { _ }, does the next action is guarantee to be ran after the last SKAction.run block's instruction executes (and what if it itself perform asynchronous calls) ?
Bonus question: Am I right to set self as weak in closure capture lists ? As I do not want it to be kept as a strong reference when dismissing the game scene.
EDITED: Well, something like that could do the job but the second fade starts before the first one ends ...
private func runSampleSequence() {
// I want node1 to fadeIn
// then node2 to fadeIn
// then node1 to fadeOut
// then node2 to fadeOut
self.run(SKAction.sequence([
node1.fadeIn(withDuration: 1),
node2.fadeIn(withDuration: 2),
node1.fadeOut(withDuration: 4),
node2.fadeOut(withDuration: 3)
]))
}
by returning actions from an SKNode extension :
extension SKNode {
func fadeIn(withDuration: TimeInterval) -> SKAction {
let action = SKAction.run {
self.run(SKAction.fadeIn(withDuration: withDuration))
}
return action
}
//TODO: Extend for each base SKAction ...
}
回答1:
Can I achieve this ? I want the business code to appear as clear as possible.
You can use swift extensions to make your code more readable. For example:
extension SKNode {
//TODO: Rename method and variable to be more expressive
func runAnimationName(node2:SKNode) {
self.run(SKAction.sequence([
SKAction.fadeIn(withDuration: 1),
SKAction.run {
node2.run(SKAction.fadeIn(withDuration: 2))
},
SKAction.fadeOut(withDuration: 4),
SKAction.run {
node2.run(SKAction.fadeOut(withDuration: 3))
}
]))
}
}
How does SKAction.sequence(_:) behave exactly when using SKAction.run { _ }, does the next action is guarantee to be ran after the last SKAction.run block's instruction executes (and what if it itself perform asynchronous calls) ?
Yes, actions are executed sequentially. Also If there is an asynchronous call inside the SKAction
run block the next SKAction
will not wait for that asynchronous call to end.
Bonus question: Am I right to set self as weak in closure capture lists ? As I do not want it to be kept as a strong reference when dismissing the game scene.
You are not using self
inside your blocks, so you can remove the [weak self]
.
回答2:
Well, one solution is to group each SKAction.run { _ } section with an SKAction.wait(_:) with the same duration (which is ugly, the sequence call should handle it itself) :
extension SKNode {
func fadeIn(withDuration: TimeInterval) -> SKAction {
let action = SKAction.group([
SKAction.wait(forDuration: withDuration),
SKAction.run {
self.run(SKAction.fadeIn(withDuration: withDuration))
}
])
return action
}
//TODO: Extend for each base SKAction ...
}
来源:https://stackoverflow.com/questions/48757257/spritekit-sequencing-multiple-skactions-running-on-multiple-sknodes