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
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.