【问题标题】:Remove SKAction and restore node state移除 SKAction 并恢复节点状态
【发布时间】:2019-05-15 19:28:33
【问题描述】:

期望的行为是:当从节点中删除一个动作时(例如removeAction(forKey:)),它会停止动画并且丢弃由动作引起的所有更改,因此节点返回到之前的状态。换句话说,我想实现类似于CAAnimation的行为。

但是当 SKAction 被删除时,节点仍然保持变化。这不好,因为要恢复它的状态,我需要确切地知道删除了哪些操作。如果我随后更改操作,我还需要更新节点状态恢复。

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

【问题讨论】:

  • 你不想在这方面使用 SKActions。研究 GameplayKit 及其状态机功能

标签: swift animation sprite-kit skaction sknode


【解决方案1】:

感谢有用的评论和回答,我找到了自己的解决方案。我认为状态机在这里有点太重了。相反,我创建了一个包装器节点,其主要目的是运行动画。它还有一个状态: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
    }
}

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

【讨论】:

    【解决方案2】:

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

    首先声明你的玩家/角色在你的场景中的状态

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

    然后你需要为这些状态中的每一个创建一个类,在这个例子中我将只向你展示Idle

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-17
      • 1970-01-01
      • 2012-12-31
      • 1970-01-01
      • 2020-03-27
      • 1970-01-01
      • 2021-11-23
      • 1970-01-01
      相关资源
      最近更新 更多