Electricity in Unity — Part 3 of 3
Parts 1 and 2 of this series give details on creating the single arc and the Jacob’s Ladder effect. Links to parts 1 and 2 are at the end of this article.
The conclusion takes on the lightning bolt effect. This is the real reason I wanted to do this in the first place. I’ve seen a few takes on this effect, and wanted to try my hand at it.
Lightning Bolt Scene
A cloud particle system is placed above the player’s platform. There is a point light source, and an empty GameObject for a lightning origin point in the particle system:
There are three empty GameObjects arranged below the particle system close to the player’s platform as lightning aiming points.
Lightning Bolt Strategy
The bolts have a main trunk with other bolts branching off. The main trunk construction is similar to the arc in part 1, with a beginning and end point broken up into sections with points placed using perturbed vectors:
Recursive Bolt Generation
At each intermediate point, a bolt branching off from the main trunk might be generated. Since endless, stack-busting recursion is not on my bucket list, there are a couple of elements in play to ensure that this doesn’t happen.
Branch Segments
Each branch has fewer segments than its parent branch, guaranteeing that the branching will end. This also lends to a balanced appearance of the final result.
Chance of Branching
As a bolt is generated, it is passed a percentage chance of a branch happening at each of the points along its path. Each branch that is spawned from a path point gets a reduced chance of branches happening at its path points.
Branch Angles
When a branch is formed, it is at a random angle between two set angles (10° and 35°). Half the time this angle is negated, preventing the bolts and branches from being one-sided.
Line Renderers
Up to this point, I have only needed one LineRenderer, since the arcs and Jacob’s Ladders are continuous bolts of electricity. Each branch of the lightning bolt will need its own LineRenderer, so a LineRenderer is put in a prefab that’s instantiated for each branch. These LineRenderers are stored in a list when instantiated, and handled all together.
Bolt Length and Width
Each bolt has its starting width reduced from its parent’s value, and each bolt’s ending width is reduced by a percentage from its starting width. The bolt’s length is ¼ of its parents length.
Animation
To keep the lightning bolts animations varied, I created a series of lists of floating point values that serve as flash sequences:
The values represent fractions of a second, and range from 0.01 to 0.25. The lists vary from 5 to 7 members, and represent lightning bolt on times and off times. As each complete lightning bolt is generated, its line renderers are turned on and off according to a randomly-selected flash sequence. At the end of the last sequence, all the line renderers are turned off and the bolt’s life has concluded. Update is responsible for handling this:
Audio Problems
During playback, I wanted the lightning audio clip to play back for a certain length, and then fade out. I wanted the sound to continue after the bolt finished hitting. I decided on 2 seconds of play, and ½ second fade out. Sounded good enough initially. I implemented the play and fade out this way:
This caused problems when testing the effect because if you fired another bolt while the sound was still fading — very easy to end up with this situation — the new sound was quickly aborted.
The solution was pretty simple. Since I always wanted the same profile — 2 seconds play with ½ second of fade, I brought the audio file into Audacity, cropped it to 2.5 sec, and faded the last ½ second. Now all I need to do is this:
audioSrc.PlayOneShot(audioSrc.clip);
The PlayOneShot allows you to play the same clip while previous version is still playing. No need for coroutines either. It’s nice when a better solution is also simpler.
The final “problem” with the lightning bolts was the monotonous result of playing the same thunder audio clip each and every time. I found some great sounds at https://samplefocus.com/tag/thunder, and selected about a half dozen of them, put them in a list, and randomly selected from it each time.
The Code
The code is available on GitLab, linked at the end of this article, but here are the main methods.
Activate initializes the bolt:
GenerateBolt is the recursive function that builds everything. There are quite a few settings to handle as you recurse, so that is actually called from BranchAndGenerate. If I did this again, I would put this data in a struct, update it at the end of GenerateBolt, and recurse.
Results
The results aren’t too bad:
When you have your headset on, and you teleport up right to the edge of the demo platform, and let it cut loose with bolts landing about 15 feet in front of you? Oh yeah man, that’s the ticket.
Playing The Effects
So that’s it — three different electrical effects. Rather than restrict this project to VR, I created a GameObject to control game play:
The inspector shows instructions, and three different camera locations:
To play in unity, just select what you want to see, and play directly without building and running on your VR headset. To activate the chosen effect, just press the space bar.
Of course, you can build and run on your headset. Use the left thumbstick to teleport, and the right trigger to activate the sequence you are standing near. There is a placard nearby when you start the game that has instructions on it.
GitLab
The code for all three effects is on GitLab at https://gitlab.com/pkrouse_group/electricity
Previous parts of this series…