0

I use a subclass NSOperation to obtain serial execution of SKAction as described in this question: How to subclass NSOperation in Swift to queue SKAction objects for serial execution?

I then modified the previous subclass in order to group animations for several nodes: https://stackoverflow.com/a/30600718/540780

At last, as I needed to run completion block after SKAction completed, I slightly modified the code by using an array of struct instead an array of tuples.

struct ActionData {
    let node:SKNode
    let action:SKAction
    let completion: () -> ()

    init (node:SKNode, action:SKAction, completion:() -> () = {}) {
        self.node = node
        self.action = action
        self.completion = completion
    }
}

class ActionOperation : NSOperation
{

    let _theActions:[ActionData]

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


    // Initialisation with one action for one node
    //
    // For backwards compatibility
    //
    init(node:SKNode, action:SKAction) {
        let donnees = ActionData(node: node, action: action, completion: {})
        _theActions = [donnees]

        super.init()
    }

    init (lesActions:[ActionData]) {
        _theActions = lesActions

        super.init()
    }

    func checkCompletion() {
        _numberOfOperationsFinished++

        logGeneral.debug(">> Block completed: \(self._numberOfOperationsFinished)/\(self._theActions.count) " + self.name!)

        if _numberOfOperationsFinished ==  _theActions.count {
            self.executing = false
            self.finished = true
            logGeneral.debug("Operation Completed: " + self.name!)
        }

    }

    override func start()
    {
        if cancelled {
            finished = true
            return
        }

        executing = true

        if name == nil {
            name = "unknown"
        }


        _numberOfOperationsFinished = 0
        var operation = NSBlockOperation()

        var compteur = 0
        for actionData in _theActions {
            compteur++

            var actionName = "???"
            if let name = actionData.node.name {
                actionName = name
            }

            logGeneral.debug("operation : \(compteur)/\(self._theActions.count) " + self.name! + " " + actionName)
            operation.addExecutionBlock({
                actionData.node.runAction(actionData.action,completion:{
                    actionData.completion()
                    self.checkCompletion() })
            })
        }

        logGeneral.debug("Execute: " + self.name!)
        NSOperationQueue.mainQueue().addOperation(operation)

    }
}

The main idea is to elaborate animations by adding data of ActionData type, append them to an array and transmit this array to the ActionAnimation object.

In some random cases, some runAction doesn complete: they start but some of them randomly do not complete. Here is a typical log where 6 block started but only 5 completed:

start(): operation : 1/6 ???
start(): operation : 2/6 suppressionPion1
start(): operation : 3/6 suppressionPion2
start(): operation : 4/6 suppressionPion3
start(): operation : 5/6 suppressionPion4
start(): operation : 6/6 suppressionGroupe1
start(): Execute: animerSupprimer
checkCompletion(): >> Block completed: 1/6
checkCompletion(): >> Block completed: 2/6
checkCompletion(): >> Block completed: 3/6
checkCompletion(): >> Block completed: 4/6
checkCompletion(): >> Block completed: 5/6

In this case only one runAction failed to completed, in other case 2, 3 or none.

I really don't understand where the problem come from.

UPDATE

In some cases the app crashed with EXC_BAD_ACCESS on main thread. This seems to be related to SKCSprite::update(double): enter image description here

Community
  • 1
  • 1
Dominique Vial
  • 3,729
  • 2
  • 25
  • 45
  • 1
    I am not sure why you are going through all this work with NSOperations just to get animation sequences. Have you looked at `SKAction.sequence` and `SKAction.group`? You can have actions run in a `sequence`, or `group` actions together, or even include groups in your sequence. You could even put a completion at the end of your sequence using `SKAction.runBlock`. They are all described in the [SKAction docs](https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKAction_Ref/index.html#//apple_ref/occ/cl/SKAction). – justinpawela Jun 06 '15 at 17:41
  • I need to use `NSOperations` to sequence execution of animations and computations on different nodes. Maybe there is a far easy way to achieve this and I don't see it. But this is not the purpose of this question. – Dominique Vial Jun 07 '15 at 08:10
  • To @user2194039's question, you cannot add to an `SKAction` sequence after it has begun running. The approach referenced in this question ([How to subclass...](http://stackoverflow.com/q/28373490/540780)) makes that possible. – Kevin Owens Jun 07 '15 at 23:46

0 Answers0