Create a Lightning Animation Using SpriteKit in SwiftUI | by Nikhil Vinod | Jul, 2022

Create a lightning experience in SwiftUI using Particle Emitters and other SpriteKit magic potions

Photo by Johannes Plenio on Unsplash

Before even starting to code our lightning we will try to picture a bolt of lightning. Each and every lightning bolt strikes glows and then fades and it’s mostly a jagged set of lines. Mostly the lightning bolt set is a single jagged set of lines without branches and few can have branches as well. And for those who have missed the first part here is it.

Now let’s see how we will use the SKScene to set up our bolt node.
The coordinate system of the SKScene is something like the one below.

Coordinate System

Now I believe you might have got some idea where we will be starting our lightning and ending our lightning at.

Let’s start!!!

Draw Bolt ⚡️

For lightning to happen we need to start with a bolt. And then make it more realistic by adding more and more bolts. So let us write our very first function to create the bolt. We will name the function as addBolt and this function will take 2 inputs currentPoint and latestPoint .
We will use the currentPoint to set the position of our drawing pen. We will use the latestPoint to set the endpoint of the line drawn from the currentPoint to latestPoint . By using the currentPoint and latestPoint we get the bolt path.

But that is not enough we need to draw the line also. For that, we will use SKShapeNode and set the bolt path to the SKShapeNode . For detailing we will give the shape node a glowWidth and a strokeColor . Once that is done we will add that shape node as the child of our SKScene.

Generate Random Points for Bolts 🎲

Now that we have seen how to create a bolt node. We will see how to generate random points for the bolts for that they will look progressive and more realistic. For this, we will use two randomizers one for Bolt length and the other for the next Bolt angle.

For Bolt Angle, I will give some starter pack info on Unit Circle-
In the unit circle, for any angle θ, we can represent the point P on a unit circle having coordinates (x,y) as:

y = sinθ
x = cosθ

Now if the circle isn’t a unit circle and it has a radius of r we will write

y = r*sinθ
x = r*cosθ

Now we cant keep a fixed angle to generate the (x,y) points because it will look like a mirror looped zig-zag line. So instead of keeping a fixed angle, we will use a random function to generate the next bolt angle like the below code.

let randomAngle = Double.random(in: -180..<180)

Now that we have a random angle we will generate our (x,y) points. And also same as our angle, we will have to randomize the bolt length also.
We will keep the range of the randomizer as min=5 and max=30.

let randomBoltLength = Double.random(in: 5..<30)

For getting our x point we will multiply the cos(randomAngle) value with the generated randomBoltLength and add the last bolt point’s x value
For getting our y point we will be needing a positive value of a multiplied randomBoltLength and sin(randomAngle) and decrement that value from the last bolt point’s y value. Why decrement because like I said before in the starting, the top part points will be UIScreenHeight and as we go down it will be decrementing.

Start Lightning ⚡️

At this point, we have all the necessary functions to kick start the lightning. We will create the startLightning functions with input parameters as currentPoint , isBranchNode and currentIteration . We will be using the startLightning function recursively to generate complete lightning. The function will have a variable named latestPoint which will hold the randomly generated bolt points. Also another variable named randomedBranchNode which will be assigned a random integer value between 0…100. We will come back to this later.

Before continuing we will just comment out this part
self.addChild(rainEmitter)
So that we can have complete focus on creating the lightning. 🙂

To make the lighting progressive we will use asyncAfter with a random generated time.

DispatchQueue.main.asyncAfter(deadline: .now() + Double.random(in: 0..<0.03))

Core Logic
Now we will set up the recursive logic we will also set our branching logic also. Here as a basic check, we will check if the bolt points y have crossed less than 0, which means that the lightning has reached the ground or it’s done. And if the bolt point y hasn’t reached or crossed 0 value then we will repeat the function call.

Branching Logic
A bolt of lightning can have multiple branches. And to select the branching nodes we will use the generated values ​​from the randomedBranchNode variable. If the currentIteration and randomedBranchNode holds the same value then we will call the startLightning function again with isBranchNode parameter as true. For a branch node, we cant definitely not use the currentPoint because that’s been consumed for the un-branched one. And if its a branched node then we will keep updating a variable named lastBranchNodePoint . And the lastBranchNodePoint will hold the latestPoint as its value. Below is a briefing of the whole setup.

Branched and Not Branched

The final function for the startLightning function will look like below.

At this point, the final animation will look like the below.

After setting up the lightning

Add Bolt Flasher and Lightning Flasher 🔦

For this, we will be using the SKAction sequence to run the Bolt Flasher and Lightning Flasher animation. For flashing the Bolt we will be toggling the SKScene’s alpha value from 1 to 0.5 on repeat. We will call this function boltFlasher . This function will take glowCount as its input. The glowCount will the factor for the repeat duration.

After adding the boltFlasher we will call this function in our startLightning function again with a random generator for the glowCount .

let randomBlinkCounter = Int.random(in: 0...5)self.boltFlasher(glowCount: randomBlinkCounter)

Our final animation at this level will look like the below.

After adding the BoltFlasher animation

Now we will add the Lightning Flasher logic.
For this we will change the SKScene’s background color from black to white with alpha value 0.7. Also we wont be doing flash everytime. And if we do that it will look really bad. So instead of flashing everytime we will use the currentIteration count to toggle the Lightning Flasher animation.

And we will add the below lines of code for adding the lightning flasher also.
On multiples of 11 we will be adding the lightning flasher.

if currentIteration % 11 == 0 {
self.lightningFlasher(flashCount: 1)
}

Our final animation at this level will look like the below.

After adding the Lightning Flasher animation

I hope you understood how to create Lightning Effect using a recursive algorithm considering lightning branching also. In the next article(Part 3) i we will connect the Rain Animation which we created in the first part with the Lightning Animation which we just created.

Thank you and have a great day ahead. ❤

Leave a Comment