Search code examples
sprite-kitswift2ios9skemitternode

Can't re-use emitter node with different parent in iOS9


I'm trying to pool my particle emitter nodes. I re-use them when they are needed by removing them from their old parent node and adding them as a child of a SKSpriteNode at a new location. I leave the emitter node position set to 0,0 so the emitter should appear in the center of its new parent sprite node.

The emitters display correctly the first time they are added as a child to a sprite node, but simply do not show up on subsequent attempts. This all worked great in iOS8 and is only broken in iOS9 (seems like lots of particle emitter bugs in iOS9?)

Here's a basic example of my code when I need to place the particle effect:

if emitter.parent != nil {
    emitter.removeFromParent()
}

newLocationSpriteNode.addChild(emitter)

emitter.resetSimulation()

This worked perfectly in iOS8 - I could re-use my emitter nodes at new locations. In iOS9 the nodes only appear the first time this code runs and never show up again after. Do you have any insight into how I might work around this issue? Thanks!


Solution

  • I experienced the exact same problem as you described. Emitters were not visible when re-attached for the second time. Everything worked fine on ios8 though. After several hours of experimenting with different settings I almost gave up.. However, I found a solution that works now. First of all I have a pool of SKEmitterNodes which I re-use during gameplay. This method grabs an emitter from the pool (array) and adds it to the gameplay-layer (SKNode):

        func createExplosion(position: CGPoint) {
    
          let emitter = _pool.borrowDirtEmitter()
    
          emitter.resetSimulation() //Important
          emitter.position = position
          emitter.targetNode = self //Important
    
          if emitter.parent == nil {
            self.addChild(emitter)
          }
      }
    

    So "self" here is the actual node that I attach the emitter to. When the node is offscreen I clean up emitters (and other objects):

            if let dirtEmitter = childNode as? SKEmitterNode {
                if dirtEmitter.parent != nil {
                    dirtEmitter.removeFromParent()
                }
                dirtEmitter.targetNode = nil //Important!
                _pool.returnDirtEmitter(dirtEmitter)
            }
    

    I haven´t had the time to go into more detail yet, but the "Important" comments should give you some pointers. I will try testing out an approach using an action to remove from parent as well (after x seconds), but since I´m making a side scroller I can get away with cleaning up when emitters are offscreen for now.

    Hope this helps..