In this chapter, we will continue learning about visual effects for our game. We will be discussing particle systems, a way to simulate fire, waterfalls, smoke, and all kinds of fluids. Also, we will see the two Unity particle systems to create these kinds of effects, Shuriken, and Visual Effect Graph, the latter being more powerful than the first but requiring more hardware.
In this chapter, we will cover the following particle system topics:
All graphics and effects we have created so far use static meshes—3D models that can’t be skewed, bent, or deformed in any way. Fluids such as fire and smoke clearly can’t be represented using this kind of mesh, but actually, we can simulate these effects with a combination of static meshes, and this is where particle systems are useful.
Particle systems are objects that emit and animate lots of particles or billboards, which are simple quad meshes that face the camera. Each particle is a static mesh, but rendering, animating, and combining lots of them can generate the illusion of a fluid.
In Figure 11.1 you can see a smoke effect using particle systems on the left, and on the right, the Wireframe view of the same particles. There you can see the quads that create the illusion of smoke, which is done by applying a smoke texture to each of the particles and animating them, so they spawn at the bottom and move up in random directions:
Figure 11.1: On the left side, a smoke particle system; on the right side, the wireframe of the same system
In this section, we will cover the following topics related to particles:
Let’s start by discussing how to create our very first particle system using Shuriken.
To illustrate the creation of a particle system, let’s create an explosion effect. The idea is to spawn lots of particles at once and spread them in all directions. Let’s start with creating the Shuriken particle system and configuring the basic settings it provides to change its default behavior. To do so, follow these steps:
Figure 11.2: Particle System button
Figure 11.3: Default particle system appearance
Figure 11.4: Shape properties
10
. This will make the particles move faster.0.5
. This specifies how long a particle will live. In this case, we have given a lifetime of half a second. In combination with the speed (10 meters per second), this makes the particles disappear after moving 5 meters:Figure 11.5: Main Particle System module
0
. This property specifies how many particles will be emitted per second, but for an explosion, we actually need a burst of particles, so we won’t emit particles constantly over time in this case.100
:Figure 11.6: Emission module
1
and uncheck Looping. In our case, the explosion won’t repeat constantly; we just need one explosion:Figure 11.7: Looping checkbox
Figure 11.8: Particle system playback controls
Figure 11.9: Stop Action set to Destroy
3
. This will make the particles bigger so they seem denser:Figure 11.10: Particle system Start Size
0
and 360
in the two input values that appeared after step 14. This allows us to give the particles a random rotation when they spawn to make them look slightly different from each other:Figure 11.11: Random Start Rotation
Explosion
.Figure 11.12: Particle system material shader
Figure 11.13: Smoke particle texture
Figure 11.14: Surface options for particles
Figure 11.15: Particle material settings
Figure 11.16: Result of the previous settings
With those steps, we have changed how the particles or billboards will spawn (using the Emission module), in which direction they will move (using the Shape module), how fast they will move, how long they will last, how big they will be (using the Main module), and what they will look like (using the Renderer module). Creating particle systems is a simple case of properly configuring their different settings. Of course, doing it properly is an art on its own; it requires creativity and knowledge of how to use all the settings and configurations they provide. So, to increase our skillset, let’s discuss some advanced modules.
Our system looks nice, but we can improve it a lot, so let’s enable some new modules to increase its quality:
Figure 11.17: Enabling the Color over Lifetime module
Figure 11.18: Color over Lifetime gradient editor
0
. Do the same with the top-right marker, as shown in the following screenshot. Now you should see the particles fading away instead of popping out of existence when the explosion is finishing:Figure 11.19: Fade-in and fade-out gradient
0.1
. This will make the particles slowly stop instead of continuing to move:Figure 11.20: Dampen the velocity to make the particles stop
-90
and 90
. Remember that you should set the value in Random Between Two Constants by clicking on the down-pointing arrow to the right of the property. Now the particles should rotate during their lives to simulate more motion:Figure 11.21: Random rotation velocity
Some of these effects will be very subtle given the short Lifetime we set in the Main Module when we just created the particle. Feel free to increase the Lifetime value to see those effects in more detail, but consider that this could lead to an excessive number of particles if you spawn them frequently, reducing performance. Just be wary about how they impact your performance when tweaking those values.
As you can see, there are lots of extra modules that can be enabled and disabled to add layers of behavior on top of the existing ones, so again, use them creatively to create all kinds of effects. Remember that you can create Prefabs of these systems to replicate them all over your scene. I also recommend searching for and downloading particle effects from the Asset Store to see how other people have used the same system to create amazing effects. Seeing a variety of different systems is the best way to learn how to create them, and that is what we are going to do in the next section: create more systems!
As we said, the best way to learn how to create particle systems is to keep looking for already-created particle systems and explore how people have used the various system settings to create completely different simulations.
In this section, we will learn how to create the following effects using particle systems:
Let’s start with the simplest one, the waterfall effect.
In order to do this, follow these steps:
5
in the Shape module. This will make the particles spawn along a line of emission:Figure 11.22: Edge shape
50
.3
and the Start Lifetime to 3
:Figure 11.23: Main module settings
Figure 11.24: Gravity Modifier in the Main module
Explosion
material we created previously for this system:Figure 11.25: Explosion particle material
Figure 11.26: White to light blue gradient
As a challenge, I suggest you add a little particle system where this one ends to create some water splashes, simulating the water colliding with a lake at the bottom. Now we can add this particle system to one of the hills of our scene to decorate it, like in the following screenshot. I have adjusted the system a little bit to look better in this scenario. I challenge you to tweak it by yourself to make it look like this:
Figure 11.27: The waterfall particle system being applied to our current scene
Now, let’s create another effect: a bonfire.
In order to create a bonfire, do the following:
Figure 11.28: Particles texture sprite sheet
Figure 11.29: A material with a particle sprite sheet
4
in X and 4
in Y. After this, you should see the particles swapping textures:Figure 11.30: Enabling Texture Sheet Animation
0
and Start Size to 1.5
in the Main module.0.5
in Shape.Figure 11.31: Parenting particle systems
0
and Radius to 0.5
in the Shape module.The system should look like this:
Figure 11.32: Result of combining fire and smoke particle systems
As you can see, you can combine several particle systems to create a single effect. Take care when doing this because it’s easy to emit too many particles and affect the game’s performance. Particles are not cheap and may cause a reduction in the game’s FPS (Frames Per Second) if you are not cautious with them.
So far, we have explored one of the Unity systems that you can use to create these kinds of effects, and while this system is enough for most situations, Unity recently released a new one that can generate more complex effects, called Visual Effect Graph. Let’s see how to use it and see how it differs from Shuriken.
The particle system we have used so far is called Shuriken, and it handles all calculations in the CPU. This has both pros and cons. A pro is that it can run on all possible devices that Unity supports, regardless of their capabilities (all of them have CPUs), but a con is that we can exceed CPU capabilities easily if we are not cautious with the number of particles we emit. Modern games require more complex particle systems to generate believable effects, and this kind of CPU-based particle system solution has started to reach its limit. This is where the Visual Effect Graph comes in:
Figure 11.33: On the left, a massive particle system, and on the right, an example of a Visual Effect Graph
Visual Effect Graph is a GPU-based particle system solution, meaning that the system is executed in the video card instead of the CPU. That’s because video cards are far more efficient at executing lots and lots of little simulations, like the ones each particle of a system needs, so we can reach far higher orders of magnitude in the number of particles with the GPU than we can with the CPU. The con here is that we need a fairly modern GPU that has compute shader capabilities to support this system, so we will exclude certain target platforms using this system (forget about most mobile phones), so use it if your target platform supports it (mid- to high-end PCs, consoles, and some high-end phones).
In this section, we will discuss the following topics of Visual Effect Graph:
Let’s start by seeing how we can add support for Visual Effect Graph in our project.
So far, we have used lots of Unity features that were already installed in our project, but Unity can be extended with a myriad of plugins, both official and third-party. Visual Effect Graph is one of those features that needs to be independently installed if you are using Universal Render Pipeline (URP). We can do that using the Package Manager, a Unity window dedicated to managing official Unity plugins.
Something to think about when you are installing those packages is that each package or plugin has its own version, independent of the Unity version. That means that you can have Unity 2022.1 installed, but Visual Effect Graph 13.1.8 or whatever version you want, and you can actually update the package to a newer version without upgrading Unity. This is important because some versions of these packages require a minimum version of Unity—for example, Visual Effect Graph 13.1.8 requires Unity 2022.1 as a minimum. Moreover, some packages depend on other packages and specific versions of those packages, so we need to ensure we have the correct versions of every package to ensure compatibility. To be clear, the dependencies of a package are installed automatically, but sometimes we can have them installed separately, so in that scenario, we need to check the required version. It sounds complicated, but it is simpler than it sounds.
At the time of writing this book, in order to get Visual Effect Graph working properly we need version 13.1.8, and also we need the same version of Universal RP. Yes, Universal RP is another feature you can install using the Package Manager, but as we created the project using the Universal RP template, it was already installed for us with the proper version. With that in mind, let’s install the Visual Effect Graph as follows:
Figure 11.34: Package Manager location
Figure 11.35: Package Manager Unity Registry mode
Figure 11.36: Package version selector
Figure 11.37: Visual Effect Graph package
Now that we have installed Visual Effect Graph, let’s create our first particle system using it.
The method to create a particle system using Visual Effect Graph is similar to a regular Particle System. We will chain and configure modules as parts of the behavior of the particles, each module adding some specific behavior, but the way we do it is very different than with Shuriken. First, we need to create a Visual Effect Graph, an asset that will contain all the modules and configurations, and then make a GameObject that will execute the Graph asset to spawn particles. Let’s do that with the following steps:
Figure 11.38: Visual Effect Graph
Figure 11.39: Empty GameObject creation
Figure 11.40: Adding a component to the Visual Effect Graph
Figure 11.41: Visual Effect using the previously created Visual Effect asset
Figure 11.42: Default Visual Effect asset results
Now that we have a base effect, let’s create something that requires a lot of particles, such as dense rain. Before doing so, we will explore some core concepts of Visual Effect Graph. If you double-click the Visual Effect asset, you will see the following editor:
Figure 11.43: Visual Effect Graph editor window
This window is composed of several interconnected nodes, generating a flow of actions to be executed. As with the Shader Graph, you can navigate this window by keeping the Alt key (Option on Mac) pressed and dragging with the mouse the empty areas of the graph. At first, it seems similar to the Shader Graph, but it works a little bit differently, so let’s study each section of the default graph.
The first area to explore is the dotted one that contains three nodes. This is what Unity calls a System. A System is a set of nodes that defines how a particle will behave, and you can have as many as you want, which is the equivalent of having several particle system objects. Each System is composed of Contexts, the nodes inside the dotted area, and in this case, we have Initialize Particle, Update Particle, and Output Particle Quad. Each Context represents a different stage of the particle system logic flow, so let’s define what each Context in our graph does:
Inside each Context, apart from some base configurations, we can add Blocks. Each Block is an action that will be executed in the Context. We have actions that can be executed in any Context and then some specific Context actions. As an example, we can use an Add Position Block in the Initialize Particle Context to move the initial particle position, but if we use the same Block in the Update Particle Context, it will move the particle constantly. So basically, Contexts are different situations that happen in the life of the particle, and Blocks are actions that are executed in those situations:
Figure 11.44: A Set Velocity Random Block inside the Initialize Particle Context. This sets the initial velocity of a particle
Also, we can have Standalone Contexts, Contexts outside Systems, such as Spawn. This Context is responsible for telling the System that a new particle needs to be created. We can add Blocks to specify when the context will tell the system to create the particle, such as at a fixed rate over time, bursts, and so on. The idea is that Spawn will create particles according to its Blocks, while a System is responsible for initializing, updating, and rendering each of them, again, according to the blocks we set up inside each one of those Contexts.
So, we can see that there are lots of similarities with Shuriken, but the way to create a system here is quite different. Let’s reinforce this by creating a rain effect, which will require lots of particles—a nice use case for Visual Effect Graph.
In order to create this effect, do the following:
10000
:Figure 11.45: Initialize Particle Context
10000
:Figure 11.46: Constant Spawn Rate Block
0
, -50
, and 0
and 0
, -75
, and 0
respectively in the Set Velocity Random block in the Initialize Particle Context. This will set a random velocity pointing downward for our particles:Figure 11.47: Set Velocity Random Block
Figure 11.48: Adding blocks
-50
, 0
, and -50
and 50
, 0
, and 50
respectively. This will define an initial area in which to randomly spawn the particle.0
, -12.5
, and 0
and 100
, 25
, and 100
respectively. This will define the area where the particles should be visible. Particles can actually move outside this area, but it is important to render the particles only in the areas we are interested in them being visible.Search Frustum culling
on the internet for more information about bounds.
Figure 11.49: Configuring blocks
Figure 11.50: Visual Effect playback controls
Figure 11.51: Another way to display the Visual Effect playback controls
Figure 11.52: VFX asset saving controls
100
, 37
, and 100
. Remember that you need to change the Position of the Transform component for this:Figure 11.53: Setting a Transform position
0.5
. This will make the particles have a shorter life, ensuring that they are always inside the bounds:Figure 11.54: Set Lifetime Random Block
Figure 11.55: VFX Graph Main Texture
Figure 11.56: Additive mode of VFX Graph
Figure 11.57: Deleting a block
0.25
, 1.5
, and 0.25
. This will stretch the particles to look like falling drops:Figure 11.58: Set Scale Block
Figure 11.59: Rain results
We have just modified lots of different properties of the Visual Effect Graph, but if you want to have two instances of the same Visual Effect Graph, but with slight differences, I recommend you look at the Blackboard feature, which will allow you to expose properties in the Inspector. For example, you can make less dense rain on another scene, making the spawn rate lower, or change the particle color to make acid rain, all using the same graph, but let’s keep things simple for now.
The Blackboard feature is also present in Shader Graph.
From here, you can experiment by adding and removing Blocks from the Contexts as you wish, and again, I recommend you look for already-created Visual Effect Graphs to find ideas for other systems. Actually, you can get ideas for Visual Effect Graph by looking at effects made in Shuriken and using the analogous blocks. Also, I recommend you search for the Visual Effect Graph documentation online or at: https://docs.unity3d.com/Packages/[email protected]/manual/index.html to learn more about this system. You can also access the documentation of any Unity Package by clicking the View Documentation button in the Package Manager while the package is selected.
Figure 11.60: Package Manager documentation link
Now that we have learnt how to create different visual effects, let’s see how to use them via scripting to achieve effects that react to what’s happening in the game.
Visual feedback is the concept of using different VFX, such as particles and a VFX graph, to reinforce what is happening. For example, say right now we are shooting our weapon, and we know that this is happening because we can see the bullets. However, it doesn’t feel like a real shooting effect, because a proper shooting effect should have a muzzle effect on the tip of our gun. Another example would be the enemy dying—it just disappears with no animation! That doesn’t feel as satisfying as it could be. We can instead add a little explosion (considering they are robots).
Let’s start making our enemies spawn an explosion when they die by doing the following:
Destroy
in the main module).Enemy
prefab called ExplosionOnDeath
. This will be responsible for spawning the particles Prefab when the enemy dies.GameObject
type called particlePrefab
and drag the explosion Prefab to it.You may be expecting to add the explosion spawning to the Life
component. In that case, you are assuming that anything to do with life will spawn a particle when dying, but consider scenarios where characters die with a falling animation instead, or maybe an object that just despawns with no effect whatsoever. If a certain behavior is not used in most scenarios, it is better to code it in a separate optional script to allow us to mix and match different components and get the exact behavior we want.
Life
component and subscribe to its onDeath
event.listener
function, spawn the particle system in the same location:Figure 11.61: The explosion spawner script
The Visual Scripting version would look like this:
Figure 11.62: The explosion spawner visual script
As you can see, we are just using the same concepts we learned about in previous chapters but combining them in new ways. This is what programming is all about.
Let’s continue with the muzzle effect, which will also be a particle system, but we will take another approach this time:
Figure 11.63: The muzzle parented to the weapon
ParticleSystem
type called muzzleEffect
in PlayerShooting
.ParticleSystem
component of the muzzle to manage it.if
statement that checks whether we are shooting, execute muzzleEffect.Play();
to play the particle system. It will automatically stop and is short enough to finish between key presses:Figure 11.64: The muzzle parented to the weapon
The Visual Scripting version’s additional nodes and variables would be the following:
Figure 11.65: The muzzle playing visual script
Finally, we need to play the muzzle effect also on the AI while shooting by doing the following:
PlayerShooting
, create a field of the ParticleSystem
type called muzzleEffect
in EnemyFSM
.Shoot
method, add the line muzzleEffect.Play();
at the end of the method to play the particle system:Figure 11.66: The muzzle playing C# script
The Visual Scripting version’s additional nodes for the Attack State
and Attack Base
will be:
Figure 11.67: The muzzle playing script for the Attack State
Remember to add those nodes to both attack states and to add the muzzleEffect
variable to the AI Variables component.
In this chapter, we discussed two different ways to create particle systems: using Shuriken and VFX Graph. We used them to simulate different fluid phenomena, such as fire, a waterfall, smoke, and rain. The idea is to combine particle systems with meshes to generate all the possible props needed for your scene. Also, as you can imagine, creating these kinds of effects professionally requires you to go deeper. If you want to dedicate yourself to this (another part of the job of a technical artist), you will need to learn how to create your own particle textures to get the exact look and feel you want, code scripts that control certain aspects of the systems, and several other aspects of particle creation. However, that is outside the scope of the book.
Now that we have some rain in our scene, we can see that the sky and the lighting in the scene don’t really reflect a rainy day, so let’s fix that in the next chapter!