Hey all, as promised I took some time to put together a write-up on the basics around how these effects have been produced.
Just to cover the obvious questions that may be on the tip of your tongue, for this example I’m using game objects in Unity rather than shader math.
There’s two reasons for this:
- To make the simulation quick to adjust and play with
- To make it approachable and adjustable by developers who might have less experience in this area
This will mean that the implementation is far from optimal and would need some performance improvements – of which plenty can be made – to use it within an existing game with this amount of particles (ideally by implementing this in shader math, which is very achievable).
The other obvious question is: “How adjustable is this?” The beauty of something like this is that it’s purely a product of randomness and math, and by throwing new conditions or values in here it’s fairly quick and easy to see how the basic principle can be expanded upon:
- Adding anti-particles
- Seeding the screen with images
- Adding gravity, perhaps in the centre or on the outside
- Or possibly adding currents
The core of the simulation is a two-dimensional grid of floats representing the signal (or “energy”) that the particles use to determine their route of travel. This array is kept within a controller script referenced by all particles created by the controller.
This is similar to the way that ants move and use smell to leave routes for their fellow colony members. Wikipedia have an article on the topic here.
Every tick, the particle chooses which direction to travel based on the strength of the energy in this grid, in most cases picking the cell with the strongest energy. Using these values, the particles’ movement across the grid is determined; they can choose to go forward, forward and to the left, or forward and to the right.
If you watch an individual particle, you’ll see what I mean:
As the particles have direction, choosing to go left or right is applied as a steady turn and not a hard change in direction, meaning they don’t specifically follow the grid cells. In fact, I have a normal float-based position system which is then computed down to grid positions (shown below) to allow more fluid movement. A bit of random is thrown in here to also add an organic feel to the particles’ movement, shown in the code example below:
After the particle’s direction has been determined for the tick, its movement is then calculated. The particles are spawned with a set velocity vector which is then rotated based on the desired changes in direction:
Some checks are then performed to ensure it stays within the circle before the particle then adds further energy to the grid for future ticks:
This pretty much covers the particles’ logic. Obviously this script can be attached to just about any Unity game object; for my visual, I went with a circle sprite and a trail component, and the rest of the visual style is delivered by bloom.
The second section is the controller logic which has to manage and manipulate this energy every frame. There are two key sources of data that drive the simulation: the current energy levels and the next energy levels. The next energy levels are where all changes are made by particles during a frame, which are then applied to the current energy levels during the controller’s tick.
During the controller’s tick, the first step is to iterate through every cell of the grid and calculate the next amount of energy that should be added to the current energy. There is a calculation involved as we take don’t just take the energy from that cell, but instead an average of the energy from that cell and its adjacent cells. This causes the energy to partially bleed when applied and softens the impact of particles on the grid.
Once we have the cell’s next energy, we apply this to the cell’s current energy, which all particles read during their tick. We also apply a small reduction to these values through bleed-off to cause the energy to fade over time.
Initially, I started by zeroing the grid’s values at the end of every frame, but by adding random noise to these values instead, it caused several random behaviours to emerge. The particles were more likely to fork from one another and take interesting routes.
For completeness, below are the two methods for getting and setting the energy levels on the controller. These methods convert the float position of the particles to something that is appropriate for the grid’s dimensions. If you were to change the grid size, or would want the particles operating in a larger or smaller play space, you would most certainly need to tweak these methods.
Obviously you could hand-create all the particles, but I’d recommend creating a prefab and having the controller generate instances of the prefab for the particle when it starts:
Those are the fundamentals that should get you to the position of recreating my first visuals outlined here. With a bit of experimentation I added other mechanics such as anti-particles, currents, and gravity points, and if there is enough interest in this blog post I’ll go into further details on how this code can be expanded with further mechanics and effects.
Please share your creations with me on Twitter, and feel free to ask questions either there or below. Have fun tinkering!