如何在Swift中继承NSOperation以将SKAction对象排队以进行串行执行?

时间:2015-02-06 19:41:05

标签: swift sprite-kit nsoperation

Rob提供a great Objective-C solution子类化NSOperation以实现SKAction对象的串行排队机制。我在自己的Swift项目中成功实现了这一点。

import SpriteKit

class ActionOperation : NSOperation
{
    let _node: SKNode // The sprite node on which an action is to be performed
    let _action: SKAction // The action to perform on the sprite node
    var _finished = false // Our read-write mirror of the super's read-only finished property
    var _executing = false // Our read-write mirror of the super's read-only executing property

    /// Override read-only superclass property as read-write.
    override var executing: Bool {
        get { return _executing }
        set {
            willChangeValueForKey("isExecuting")
            _executing = newValue
            didChangeValueForKey("isExecuting")
        }
    }

    /// Override read-only superclass property as read-write.
    override var finished: Bool {
        get { return _finished }
        set {
            willChangeValueForKey("isFinished")
            _finished = newValue
            didChangeValueForKey("isFinished")
        }
    }

    /// Save off node and associated action for when it's time to run the action via start().
    init(node: SKNode, action: SKAction) {

    // This is equiv to ObjC:
    // - (instancetype)initWithNode(SKNode *)node (SKAction *)action
    // See "Exposing Swift Interfaces in Objective-C" at https://developer.apple.com/library/mac/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_35

        _node = node
        _action = action
        super.init()
    }

    /// Add the node action to the main operation queue.
    override func start()
    {
        if cancelled {
            finished = true
            return
        }

        executing = true

        NSOperationQueue.mainQueue().addOperationWithBlock {
            self._node.runAction(self._action) {
                self.executing = false
                self.finished = true
            }
        }
    }
}

要使用ActionOperation,请在客户端类中实例化NSOperationQueue类成员:

var operationQueue = NSOperationQueue()

在init方法中添加以下重要行:

operationQueue.maxConcurrentOperationCount = 1; // disallow follow actions from overlapping one another

然后当您准备好向其添加SKActions时,它们会连续运行:

operationQueue.addOperation(ActionOperation(node: mySKNode, action: mySKAction))

您是否需要在任何时候终止操作:

operationQueue.cancelAllOperations() // this renders the queue unusable; you will need to recreate it if needing to queue anymore actions

希望有所帮助!

3 个答案:

答案 0 :(得分:11)

根据the document

  

在自定义实现中,只要操作对象的执行状态发生变化,就必须为 isExecuting 键路径生成KVO通知。

  

在自定义实现中,只要操作对象的完成状态发生更改,就必须为 isFinished 键路径生成KVO通知。

所以我认为你必须:

override var executing:Bool {
    get { return _executing }
    set {
        willChangeValueForKey("isExecuting")
        _executing = newValue
        didChangeValueForKey("isExecuting")
    }
}

override var finished:Bool {
    get { return _finished }
    set {
        willChangeValueForKey("isFinished")
        _finished = newValue
        didChangeValueForKey("isFinished")
    }
}

答案 1 :(得分:1)

我想为几个节点分组动画。我首先尝试使用runAction(_:onChildWithName:)对所有操作进行分组,然后使用runAction(_:onChildWithName:)来指定哪些操作必须由节点完成。

不幸的是,存在同步问题,因为在SKAction的情况下(SKNode,SKActions)的持续时间是瞬时的。因此,我必须找到另一种方法,在一次操作中为多个节点分组动画。

然后,我通过添加元组数组addExecutionBlock来修改上面的代码。

此处提供的修改代码添加了一个功能,用于初始化多个节点的操作,每个节点都有自己的操作。

对于每个节点,在其内部运行的操作使用checkCompletion()添加到操作中的自己的块。 当一个动作完成时,执行一个调用finished的完成块以便将它们全部加入。完成所有操作后,操作将标记为class ActionOperation : NSOperation { let _theActions:[(SKNode,SKAction)] // The list of tuples : // - SKNode The sprite node on which an action is to be performed // - SKAction The action to perform on the sprite node var _finished = false // Our read-write mirror of the super's read-only finished property var _executing = false // Our read-write mirror of the super's read-only executing property var _numberOfOperationsFinished = 0 // The number of finished operations override var executing:Bool { get { return _executing } set { willChangeValueForKey("isExecuting") _executing = newValue didChangeValueForKey("isExecuting") } } override var finished:Bool { get { return _finished } set { willChangeValueForKey("isFinished") _finished = newValue didChangeValueForKey("isFinished") } } // Initialisation with one action for one node // // For backwards compatibility // init(node:SKNode, action:SKAction) { _theActions = [(node,action)] super.init() } init (theActions:[(SKNode,SKAction)]) { _theActions = theActions super.init() } func checkCompletion() { _numberOfOperationsFinished++ if _numberOfOperationsFinished == _theActions.count { self.executing = false self.finished = true } } override func start() { if cancelled { finished = true return } executing = true _numberOfOperationsFinished = 0 var operation = NSBlockOperation() for (node,action) in _theActions { operation.addExecutionBlock({ node.runAction(action,completion:{ self.checkCompletion() }) }) } NSOperationQueue.mainQueue().addOperation(operation) } }

return bloguers.replaceOne({id: bloguer.id}, bloguer,{upsert:true}, function (err){
                        console.log(err);
                        res.status(400).end();
                    });  

答案 2 :(得分:0)

在初始化期间传输的SKActionsrunAction(_:onChildWithName:)时,存在一种限制情况。

在这种情况下,此SKAction的持续时间是即时的。

根据Apple文档:

  

此动作具有即时持续时间,但对儿童执行的动作可能具有自己的持续时间。