Search code examples
swiftanimationsprite-kitskactionsknode

Remove SKAction and restore node state


Desired behavior is: when an action is removed from a node (with removeAction(forKey:) for instance) it stops to animate and all the changes caused by action are discarded, so the node returns back to pervious state. In other words, I want to achieve behavior similar to CAAnimation.

But when a SKAction is removed, the node remains changed. It's not good, because to restore it's state I need to know exactly what action was removed. And if I then change the action, I also will need to update the node state restoration.

Update:
The particular purpose is to show possible move in a match-3 game. When I show a move, pieces start pulsating (scale action, repeating forever). And when the user moves I want to stop showing the move, so I remove the action. As the result, pieces may remain downscaled. Later I would like to add more fancy and complicated animations, so I want to be able to edit it easily.


Solution

  • Thanks to the helpful comment and answer I came to my own solution. I think the state machine would be bit too heavy here. Instead I created a wrapper node, which main purpose is run the animation. It also has a state: isAimating property. But, first of all, it allows to keep startAnimating() and stopAnimating() methods close to each other, incapsulated, so it's more difficult to mess up.

    class ShowMoveAnimNode: SKNode {
        let animKey = "showMove"
    
        var isAnimating: Bool = false {
            didSet {
                guard oldValue != isAnimating else { return }
                if isAnimating {
                    startAnimating()
                } else {
                    stopAnimating()
                }
            }
        }
    
        private func startAnimating() {
            let shortPeriod = 0.2
            let scaleDown = SKAction.scale(by: 0.75, duration: shortPeriod)
            let seq = SKAction.sequence([scaleDown,
                                         scaleDown.reversed(),
                                         scaleDown,
                                         scaleDown.reversed(),
                                         SKAction.wait(forDuration: shortPeriod * 6)])
            let repeated = SKAction.repeatForever(seq)
            run(repeated, withKey: animKey)
        }
    
        private func stopAnimating() {
            removeAction(forKey: animKey)
            xScale = 1
            yScale = 1
        }
    }
    

    Usage: just add everything that should be animated to this node. Works well with simple animations, like: fade, scale and move.