【问题标题】:How to subclass NSOperation in Swift to queue SKAction objects for serial execution?如何在 Swift 中继承 NSOperation 以将 SKAction 对象排队以进行串行执行?
【发布时间】:2015-02-06 19:41:05
【问题描述】:

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

然后当您准备好向其中添加 SKAction 以使其串行运行时:

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

希望有帮助!

【问题讨论】:

  • 嗨,我已经在我的一个项目中实现了这段代码,但没有成功,因为所有ActionOperation 都没有序列化:他们没有等待前一个开始。
  • 我已经更新了帖子和代码以解决几个问题并使事情更清晰。这是来自一个有效的实现,所以你应该很高兴。
  • 那么,问题是什么?不要编辑问题来提供答案 - 发布或编辑答案。
  • 感谢这个更新的代码,我发现了问题:ActionOperation 类上的_finished 属性被初始化为true。谢谢。
  • 不客气。至于问题,它实际上仍然存在......在标题中。

标签: swift sprite-kit nsoperation


【解决方案1】:

根据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")
    }
}

【讨论】:

  • 谢谢你,成功了!我之前尝试过使用“is ...”,但显然还有其他问题,因为它当时不起作用。不过现在是……再次感谢。
【解决方案2】:

我想为几个节点组合动画。我首先尝试了上面的解决方案,将所有操作归为一个,使用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)

    }
}

【讨论】:

  • 感谢您发布更新。如果您使用以前的答案(而不是作为新答案)发布代码并阐述您的更改,那么您正在解决什么问题以及如何解决它会更清楚。我现在有点不清楚。
  • 我已经添加了原因和方法的解释。
  • 干得好。感谢您分享您的劳动成果! (有一件事……我相信变量 compteur 可能是您的故障排除遗留问题。)
  • 哦,你是对的。我已经删除了这个无用的代码。
  • 这段代码可能有问题!在我为runAction 添加完成块的改进版本中,一些runAction 没有完成。我创建了一个问题:stackoverflow.com/q/30683897/540780
【解决方案3】:

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

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

根据 Apple 文档:

这个动作有一个瞬时的持续时间,尽管对孩子执行的动作可能有它自己的持续时间。

【讨论】:

  • 有趣。我没有间接对孩子采取行动,所以没有遇到这个问题。很高兴你发现了它!
  • 我想我有答案了!使用元组数组(SKNode,SKAction)并在“start”函数中遍历该数组。
  • 我添加了一个答案,修改后的代码实现了这个解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-15
  • 1970-01-01
  • 2012-05-04
  • 1970-01-01
  • 2016-10-11
相关资源
最近更新 更多