Удалить SKAction и восстановить состояние узла - PullRequest
0 голосов
/ 14 декабря 2018

Желаемое поведение: когда действие удаляется из узла (например, с removeAction(forKey:)), оно прекращает анимацию, и все изменения, вызванные действием, отбрасываются, поэтому узел возвращается в предыдущее состояние.Другими словами, я хочу добиться поведения, аналогичного CAAnimation.

Но когда SKAction удалено, узел остается измененным.Это не хорошо, потому что для восстановления его состояния мне нужно точно знать, какое действие было удалено.И если я затем изменю действие, мне также потребуется обновить восстановление состояния узла.

Обновление:
Особая цель - показать возможный ход в игре со спичками-3.Когда я показываю ход, фигуры начинают пульсировать (scale действие, повторяющееся навсегда).И когда пользователь перемещается, я хочу перестать показывать движение, поэтому я удаляю действие.В результате куски могут оставаться уменьшенными.Позже я хотел бы добавить более причудливые и сложные анимации, поэтому я хочу иметь возможность легко их редактировать.

Ответы [ 2 ]

0 голосов
/ 14 декабря 2018

Благодаря полезному комментарию и ответу я пришел к собственному решению.Я думаю, что государственная машина была бы здесь слишком тяжелой.Вместо этого я создал узел-обертку, основной целью которого является запуск анимации.Он также имеет состояние: 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
    }
}

Использование: просто добавьте все, что должно бытьанимированные на этот узел.Хорошо работает с простыми анимациями, такими как: затухание, масштабирование и перемещение.

0 голосов
/ 14 декабря 2018

Как подсказал @ 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)
}
...