Slowing down particles of an SKEmitterNode

2019-04-02 03:47发布

问题:

I was just curious, is there a way to slowdown particle emitting along the other objects in the scene. Say you have one particle emitter, and one sprite. Sprite is moved using SKAction. An emitter is emitting. Both nodes are added to the scene. If you set self.speed to 0.5, only the sprite will slow down. Emitters will continue emitting at the same speed.

Is there a way to slowdown in some other way than using particleSpeed property, because that will change the behaviour of an emitter.

My question is inspired by this question: https://stackoverflow.com/a/41764180/3402095

回答1:

Normally a SKEmitterNode have many properties involved in it's speed (xAcceleration, yAcceleration, particleSpeedRange, particleScaleSpeed..):

private func newExplosion() -> SKEmitterNode {
        let explosion = SKEmitterNode()
        let image = UIImage(named:"spark.png")!
        explosion.particleTexture = SKTexture(image: image)
        explosion.particleColor = SKColor.brown
        explosion.numParticlesToEmit = 100
        explosion.particleBirthRate = 450
        explosion.particleLifetime = 2
        explosion.emissionAngleRange = 360
        explosion.particleSpeed = 100
        explosion.particleSpeedRange = 50
        explosion.xAcceleration = 0
        explosion.yAcceleration = 0
        explosion.particleAlpha = 0.8
        explosion.particleAlphaRange = 0.2
        explosion.particleAlphaSpeed = -0.5
        explosion.particleScale = 0.75
        explosion.particleScaleRange = 0.4
        explosion.particleScaleSpeed = -0.5
        explosion.particleRotation = 0
        explosion.particleRotationRange = 0
        explosion.particleRotationSpeed = 0
        explosion.particleColorBlendFactor = 1
        explosion.particleColorBlendFactorRange = 0
        explosion.particleColorBlendFactorSpeed = 0
        explosion.particleBlendMode = SKBlendMode.add
        return explosion
    }

So to achieve your request I think it should be possible to set the targetNode:

/**
     Normally the particles are rendered as if they were a child of the SKEmitterNode, they can also be rendered as if they were a child of any other node in the scene by setting the targetNode property. Defaults to nil (standard behavior).
     */
    weak open var targetNode: SKNode?

You can find more details to the Apple official docs.

Some code to example:

class GameScene: SKScene {
    var myNode :SKSpriteNode!
    var myEmitter : SKEmitterNode!
    override func didMove(to view: SKView) {
        myNode = SKSpriteNode.init(color: .red, size: CGSize(width:100,height:100))
        self.addChild(myNode)
        myNode.position = CGPoint(x:self.frame.minX,y:self.frame.midY)
        let changePos = SKAction.run{
            [weak self] in
            guard let strongSelf = self else { return }
            if strongSelf.myNode.position.x > strongSelf.frame.maxX {
                strongSelf.myNode.position.x = strongSelf.frame.minX
            }
        }
        let move = SKAction.moveBy(x: 10.0, y: 0, duration: 0.5)
        let group = SKAction.group([move,changePos])
        myNode.run(SKAction.repeatForever(SKAction.afterDelay(1.0, performAction:group)))
        let showExplosion = SKAction.repeatForever(SKAction.afterDelay(3.0, performAction:SKAction.run {
            [weak self] in
            guard let strongSelf = self else { return }
            strongSelf.myEmitter = strongSelf.newExplosion()
            strongSelf.addChild(strongSelf.myEmitter)
            strongSelf.myEmitter.targetNode = strongSelf.myNode
            strongSelf.myEmitter.particleSpeed = strongSelf.myNode.speed
            print("emitter speed is: \(strongSelf.myEmitter.particleSpeed)")
            strongSelf.myEmitter.position = CGPoint(x:strongSelf.frame.midX,y:strongSelf.frame.midY)
            strongSelf.myEmitter.run(SKAction.afterDelay(2.0, performAction:SKAction.removeFromParent()))
        }))
        self.run(showExplosion)
    }
    private func newExplosion() -> SKEmitterNode {
        let explosion = SKEmitterNode()
        let image = UIImage(named: "sparkle.png")!
        explosion.particleTexture = SKTexture(image: image)
        explosion.particleColor = UIColor.brown
        explosion.numParticlesToEmit = 100
        explosion.particleBirthRate = 450
        explosion.particleLifetime = 2
        explosion.emissionAngleRange = 360
        explosion.particleSpeed = 100
        explosion.particleSpeedRange = 50
        explosion.xAcceleration = 0
        explosion.yAcceleration = 0
        explosion.particleAlpha = 0.8
        explosion.particleAlphaRange = 0.2
        explosion.particleAlphaSpeed = -0.5
        explosion.particleScale = 0.75
        explosion.particleScaleRange = 0.4
        explosion.particleScaleSpeed = -0.5
        explosion.particleRotation = 0
        explosion.particleRotationRange = 0
        explosion.particleRotationSpeed = 0
        explosion.particleColorBlendFactor = 1
        explosion.particleColorBlendFactorRange = 0
        explosion.particleColorBlendFactorSpeed = 0
        explosion.particleBlendMode = SKBlendMode.add
        return explosion
    }
    func randomCGFloat(_ min: CGFloat,_ max: CGFloat) -> CGFloat {
        return (CGFloat(arc4random()) / CGFloat(UINT32_MAX)) * (max - min) + min
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        myNode.speed = randomCGFloat(0.0,50.0)
        print("new speed is: \(myNode.speed)")
    }
}
extension SKAction {
    class func afterDelay(_ delay: TimeInterval, performAction action: SKAction) -> SKAction {
        return SKAction.sequence([SKAction.wait(forDuration: delay), action])
    }
    class func afterDelay(_ delay: TimeInterval, runBlock block: @escaping () -> Void) -> SKAction {
        return SKAction.afterDelay(delay, performAction: SKAction.run(block))
    }
}

In this example you can see how the speed applied to myNode involved the speed of myEmitter everytime I touch the screen, because the emitter have the targetNode property setted with myNode , and I've also change the particleSpeed to seems a more realistic slowdown/accelleration.