Search code examples
swiftsprite-kitios9skemitternode

SKEmitterNode iOS 8 vs iOS 9 How to get the same result?


I have a game similar to fruit ninja using Swift -> SpriteKit. Everything is working fine on iOS 8 but on iOS 9 SKEmitterNode is having a bit strange behavior. This is what I get for my blade effect on both:

enter image description here

 func emitterNodeWithColor(color:UIColor)->SKEmitterNode {
let emitterNode:SKEmitterNode = SKEmitterNode()
emitterNode.particleTexture = SKTexture(imageNamed: "spark.png")
emitterNode.particleBirthRate = 3000
emitterNode.particleLifetime = 0.2
emitterNode.particleLifetimeRange = 0

emitterNode.particlePositionRange = CGVectorMake(0.0, 0.0)

emitterNode.particleSpeed = 0.0
emitterNode.particleSpeedRange = 0.0

emitterNode.particleAlpha = 0.8
emitterNode.particleAlphaRange = 0.2
emitterNode.particleAlphaSpeed = -0.45

emitterNode.particleScale = 0.5
emitterNode.particleScaleRange = 0.001
emitterNode.particleScaleSpeed = -1

emitterNode.particleRotation = 0
emitterNode.particleRotationRange = 0
emitterNode.particleRotationSpeed = 0

emitterNode.particleColorBlendFactor = 1
emitterNode.particleColorBlendFactorRange = 0
emitterNode.particleColorBlendFactorSpeed = 0

emitterNode.particleColor = color
emitterNode.particleBlendMode = SKBlendMode.Add

return emitterNode

}

let emitter:SKEmitterNode = emitterNodeWithColor(color)
emitter.targetNode = target
emitter.zPosition = 0
tip.addChild(emitter)

This is the method I am using with all the options. It is the same for both but the result is different. Any ideas how can I make the effect in iOS 9 to be the same as iOS 8 ?


Solution

  • I'm facing the exact same issue in my project. The emitter's performance is low in iOS9 (Metal version not finished?), so Apple shut off the interpolation of the drawing to get back the performance a little (The drawing rate is limited to 60 fps, anything between two frames is not rendered). My solution is to implement the tail myself, which is simple:

    class TailNode: SKSpriteNode {
    
    var tailTexture: SKTexture!
    var tailSize: CGSize! = CGSizeMake(30, 30)
    var tailColor: SKColor!
    var tailBlendMode: SKBlendMode!
    var initialAlpha: CGFloat = 0.6
    var initialScale: CGFloat = 0
    var finalScale: CGFloat = 1
    var particleLife: NSTimeInterval = 0.1
    
    var running: Bool = false
    var particleAction: SKAction!
    var lastParticle: SKSpriteNode?
    
    var battleScene: BattleScene {
        return self.scene as! BattleScene
    }
    
    convenience init(tailTexture: SKTexture, tailSize: CGSize, tailColor: SKColor, tailBlendMode: SKBlendMode, initialAlpha: CGFloat, initialScale: CGFloat, finalScale: CGFloat, particleLife: NSTimeInterval) {
        self.init(texture: nil, color: SKColor.whiteColor(), size: CGSize(width: 0, height: 0))
        self.tailTexture = tailTexture
        self.tailSize = tailSize
        self.tailColor = tailColor
        self.tailBlendMode = tailBlendMode
        self.initialAlpha = initialAlpha
        self.initialScale = initialScale
        self.finalScale = finalScale
        self.particleLife = particleLife
    
        let fadeAction = SKAction.fadeAlphaTo(0, duration: particleLife)
        let scaleAction = SKAction.scaleTo(finalScale, duration: particleLife)
        let removeAction = SKAction.removeFromParent()
        self.particleAction = SKAction.sequence([SKAction.group([fadeAction, scaleAction]), removeAction])
    }
    
    func updateWithTimeSinceLastUpdate(interval: NSTimeInterval) {
        if running {
            let particlePosition = battleScene.convertPoint(battleScene.convertPoint(self.position, fromNode: self.parent!),
                                                        toNode:battleScene.worldLayers[.UnderCharacter]!)
            if lastParticle == nil || lastParticle!.parent == nil {
                lastParticle = nil
            } else {
                let lastPosition = lastParticle!.position
                let x = lastPosition.x + (particlePosition.x - lastPosition.x)*0.5
                let y = lastPosition.y + (particlePosition.y - lastPosition.y)*0.5
                newParticleAtPosition(CGPointMake(x, y), withDelay: interval*0.5)
            }
            lastParticle = newParticleAtPosition(particlePosition, withDelay: interval)
        }
    }
    
    func newParticleAtPosition(position: CGPoint, withDelay delay: NSTimeInterval) -> SKSpriteNode {
        let myParticle = SKSpriteNode(texture: tailTexture, color: tailColor, size: tailSize)
        myParticle.colorBlendFactor = 1
        myParticle.blendMode = tailBlendMode
        myParticle.alpha = initialAlpha
        myParticle.setScale(initialScale)
        myParticle.position = position
        battleScene.addNode(myParticle, atWorldLayer: .UnderCharacter)
        myParticle.runAction(SKAction.sequence([SKAction.waitForDuration(delay), particleAction]))
        return myParticle
    }
    

    }