删除 SKAction 并恢复节点状态

Remove SKAction and restore node state

期望的行为是:当一个动作从一个节点中移除时(例如 removeAction(forKey:))它停止动画并且所有由动作引起的变化都被丢弃,所以节点 returns 返回到以前的状态。换句话说,我想实现类似于 CAAnimation.

的行为

但是当一个SKAction被删除时,节点仍然改变。这不好,因为要恢复它的状态,我需要确切地知道删除了什么操作。如果我再改变动作,我也将需要更新节点状态恢复。

更新:
特定目的是显示三消游戏中可能的移动。当我展示一个动作时,棋子开始跳动(scale 动作,永远重复)。当用户移动时我想停止显示移动,所以我删除了动作。因此,碎片可能会保持缩小。以后想加更多花哨复杂的动画,所以希望能轻松编辑。

正如@Knight0fDragon 所建议的那样,你最好使用 GKStateMachine 功能,我给你举个例子。

首先在场景中声明player/character的状态

lazy var playerState: GKStateMachine = GKStateMachine(states: [
    Idle(scene: self),
    Run(scene: self)
    ])

然后你需要为每个状态创建一个class,在这个例子中我只会给你展示Idle class

import SpriteKit
import GameplayKit

class Idle: GKState {
   weak var scene: GameScene?

    init(scene: SKScene) {
        self.scene = scene as? GameScene
        super.init()
    }

    override func didEnter(from previousState: GKState?) {
        //Here you can make changes to your character when it enters this state, for example, change his texture.
    }

    override func isValidNextState(_ stateClass: AnyClass) -> Bool {
        return stateClass is Run.Type //This is pretty obvious by the method name, which states can the character go to from this state.
    }

    override func update(deltaTime seconds: TimeInterval) {
        //Here is the update method for this state, lets say you have a button which controls your character velocity, then you can check if the player go over a certain velocity you make it go to the Run state.

       if playerVelocity > 500 { //playerVelocity is just an example of a variable to check the player velocity.
          scene?.playerState.enter(Run.self)
       }
    }
}

现在当然在你的场景中你需要做两件事,首先是将角色初始化到某个状态,否则它将保持无状态,所以你可以在 didMove 方法中做到这一点。

override func didMove(to view: SKView) {
    playerState.enter(Idle.self)
} 

最后但同样重要的是确保场景更新方法调用状态更新方法。

override func update(_ currentTime: TimeInterval) {
    playerState.update(deltaTime: currentTime)
}

感谢有用的评论和回答,我找到了自己的解决方案。我认为这里的状态机有点太重了。相反,我创建了一个包装器节点,其主要目的是 运行 动画。它还有一个状态:isAimating 属性。但是,首先,它允许 startAnimating()stopAnimating() 方法彼此靠近、封装,因此更难搞砸。

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
    }
}

用法:只需将应动画化的所有内容添加到该节点即可。适用于简单的动画效果,例如:淡入淡出、缩放和移动。