Caractacus
Caractacus

Reputation: 83

Is there a way to add a fading trail or stroke to a moving SKSpriteNode?

Still new, but slowly building my first app/game, and am slowly getting there.

I'd like to be able to add a fading streak or trail as one of my SKSpriteNodes moves. Whether it is moving due to touchesMoved or being sent back to its original spot by code. I just want to add a nice visual effect.

The only thing I can think of is calculating the distance, breaking it down into x movements, then gradually move the Sprite a little, with some fade options, and then repeat in a loop till it gets back to home base, using a lot of nested SKActions and sequences.

I know that has to be wrong because it's just so ugly.

When I look at the Sprite Kit's Particle File, it has so few options. And I'm not really sure that's what I should be looking at. I have also looked at SKAction's options, and if there's an option there I'm missing it.

Surely in Swift's huge animation library there has to be something?

Upvotes: 1

Views: 205

Answers (1)

pua666
pua666

Reputation: 336

Let's create a basic sprite and an emitter, and make the emitter a child of the sprite so that it follows it:

let sprite = SKSpriteNode(color: .white, size: CGSize(width: 20, height: 10))

let emitter = SKEmitterNode() // better to use the visual designer in Xcode...
emitter.particleLifetime = 1.0
emitter.particleBirthRate = 50.0
emitter.particleSpeed = 100.0
emitter.emissionAngleRange = .pi / 5
emitter.particleTexture = SKTexture(imageNamed: "spark")
emitter.particleScale = 0.1
emitter.particleAlphaSpeed = -1.0
emitter.emissionAngle = -.pi


sprite.addChild(emitter) // attach it to the sprite
emitter.position.x = -15 // but not to the center

scene.addChild(sprite)

sprite.run(SKAction.group([ // let's run some actions to test it:
    SKAction.sequence([
        SKAction.move(to: CGPoint(x: 200, y: 200), duration: 5),
        SKAction.move(to: CGPoint(x: 50, y: 50), duration: 5),
    ]),
    SKAction.rotate(byAngle: .pi * 2.0, duration: 10)
]))

(Click to open animated GIF if it doesn't display correctly:)

First attempt

To the casual observer, it looks fine… Except that, after some scrutiny, you'll realize what's off: the particles emitted live in the universe of the parent sprite, moving and rotating with it, even long after they were emitted! That's not right!

That's because the targetNode of the emitter is its parent, and it should be the scene!

So let's insert this line somewhere:

emitter.targetNode = scene // otherwise the particles would keep moving with the sprite

(Click to open animated GIF if it doesn't display correctly:)

Second attempt

Okay, this is a no-go: the particles now stay in the "universe" of the scene, but apparently their emission angle fails to follow that of the parent (which looks like a bug to me).

Luckily, we can attach a custom action to the emitter which keeps aligning this angle to the parent sprite:

emitter.run(SKAction.repeatForever(
                SKAction.customAction(withDuration: 1) {
                    ($0 as! SKEmitterNode).emissionAngle = sprite.zRotation + .pi
                    _ = $1
                }))

(Click to open animated GIF if it doesn't display correctly:)

Final attempt

Okay, now new particles are launched in the correct direction, and keep moving that way even if the sprite moves or rotates in the meantime. This seems to be the most realistic simulation so far, though there may still be ways to improve it by modifying the behavior of the emitter on the fly.

Sorry for the jaggy GIFs, apparently my Mac is too slow to render and capture at the same time. The animations themselves run just fine.

Upvotes: 2

Related Questions