SpriteKit - Sequencing multiple SKActions running on multiple SKNodes

天大地大妈咪最大 提交于 2020-05-31 04:44:47

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!