Search code examples
iosswiftsprite-kitparticle-system

How to add an SKEmitterNode to an SKScene (it's not showing up)?


I'm trying to add an SKEmitterNode to my game scene (SKScene). I do the following but it does not show up:

if let emitter = SKEmitterNode(fileNamed: "Explosion.sks") {
    emitter.position = CGPoint(x: 0, y: 0) // center of screen
    emitter.name = "boom"
    emitter.targetNode = self // setting this makes no difference
    emitter.zPosition = 10 // tried higher values also
    emitter.particleZPosition = 10 // tried with and without this too
    addChild(emitter)
    print("NODE ADDED")
}

Output prints "NODE ADDED" but node is nowhere to be seen. All other nodes are added normally with no problem. If I swap the code above with

let node = SKShapeNode(rectOf: CGSize.init(width: 50, height: 50))
node.position = CGPoint(x: 0, y: 0)
node.fillColor = SKColor.red
addChild(node)

it works as expected and I see a red square in the center of the screen.

As a sanity check, if I add the emitter node as the child of another visible node in the scene, it shows up as a child of the other node:

let node = SKShapeNode(rectOf: CGSize.init(width: 50, height: 50))
node.position = CGPoint(x: 0, y: 0)
node.fillColor = SKColor.red
if let emitter = SKEmitterNode(fileNamed: "Explosion.sks") {
    emitter.position = CGPoint(x: 0, y: 0) // center of screen
    emitter.name = "boom"
    emitter.targetNode = node
    node.addChild(emitter)
}
addChild(node)

But I cannot add it directly to the SKScene. I could possibly hack it using an invisible parent node, but that's just annoying. I tried wrapping it in an SKEffectNode to no avail.

iOS 10.3, iPhone 7 simulator, Swift 3


Solution

  • The answer was subtle: I had a cleanup method in my code that removed nodes that went off screen. It gets called every frame:

    self.enumerateChildNodes(withName: "*", using: { (node, stop) -> Void in
        if !self.intersects(node) {
            node.removeFromParent()
        }
    })
    

    Turns out that SKEmitterNode never "intersects" the scene, even if it's positioned directly on top of it. My initial solution was to just avoid running this check on those particular nodes:

    if !self.intersects(node) {
    

    becomes

    if !self.intersects(node) && !(node is SKEmitterNode) && !(node is SKEffectNode) {