let's say I wanna make an Aura+Thunder effect
, I can achieve the result using the Particles System
like this vid: https://www.youtube.com/watch?v=4eMZspcU5Is , or by using AnimationPlayer
, but which is more performant if there will be like 15 instance of the Aura+Thunder effect
+ other effects?
-
2\$\begingroup\$ What does your profiler tell you? \$\endgroup\$– DMGregory ♦Commented May 27, 2021 at 17:12
-
\$\begingroup\$ There is some famous quote about premature optimization that might be relevant here. \$\endgroup\$– TheraotCommented May 27, 2021 at 18:24
1 Answer
At the end it is going to depend on whether or not GPU or CPU is the bottleneck. However, the options I describe below are roughly sorted by decreasing chance of being the most performant option (under the possibly wrong assumption that the GPU is more powerful, and thus the CPU is likely the bottleneck). Which is not a replacement for testing, if you really want the most performant solution.
By the way, it also turns out to be roughly sorted by increasing effort to do within Godot (under the possibly wrong assumption that you find it easier to move things in the editor than writing code, and thus writing code is likely more effort).
Particles
Using Particles
is likely the most performant way to do it, and it turns out to be the easier to do within Godot. So start there.
The Particles
node will animate the particles on the GPU. Because of that, If you can do the effect with Particles
it is likely to be the most performant approach.
One drawback is that it is not supported on the GLES2 backend. So make sure it is supported in the target hardware.
Also, some features are only available for CPUParticles
. Which is the next stop.
CPUParticles
Assuming you had your system set up with Particles
and you need something only available for CPUParticles
, you can convert them! There will be a "Particles" option in the editor when the Particles
node is selected, and under it you find "Convert to CPUParticles".
Using CPUParticles
will minimize render calls, but it will compute all the movement on the CPU. Which means, unlike Particles
, CPUParticles
eat CPU time. But it also means they have less limitations.
Shader Material
Let us say that Particles
is not good enough, and you don't want the particles to run on the CPU. You can a shot at doing what they do: Set a shader material with a MultiMesh
(to minimize render calls) and apply displacement in the vertex function to move the instances (MultiMeshInstance
), so that all the movement is computed on the GPU.
By the way, just the fact that MultiMesh
and MultiMeshInstance
are nodes is overhead.
For randomness you can use a noise texture, for example. There is a NoiseTexture
resource type you can use.
Use INSTANCE_ID
and INSTANCE_CUSTOM
built-ins in the shader language to have it distinguish the different instances. Note: INSTANCE_ID
is not available in GLES2.
This will take advantage of the GPU for the particle animation… And it is, of course, more work to write the shader code. However, consider this option before AnimationPlayer
. By the way, this option may or may not be more performant than CPUParticles
depending on your bottleneck.
AnimationPlayer
Similarly to CPUParticles
, AnimationPlayer
will work on the CPU. Although, AnimationPlayer
is arguably the most powerful option -baring using an AnimationTree
- it has a couple significant drawback for this use case:
- It is not made for randomness. Every time the animation plays, it is the same.
- It is a lot of work to setup the animation of all the particles in the editor.
Thus, I would only suggest it if you can create the particle effect in another software and import it in such way that it comes up as an AnimationPlayer
. And even then, it would be the same every time, and it is not taking full advantage of the GPU.
By the way, animate MultiMeshInstance
to minimize render calls.
Furthermore, the power of AnimationPlayer
come from the fact that it can control other nodes. For example you can use it to play sound on cue. Heck, it can call methods!. Thus, if you really need an AnimationPlayer
, you can have it control the shader parameters of the aforementioned setup, so it is only in charge of the parts you can't do from the shader.
Just move things from code
The likely to be less performant option is to move things from the _process
. This is not far from what AnimationPlayer
does (except, you know, that is core code).
But hey, Turing and Tetris complete language! You can take advantage of Tween
to do interpolations. You can query physic bodies for collisions, you can play sounds, you can write to file and send requests to the network.
You can create the nodes from code, including making all the setups previously mentioned. So you can have code that creates an AnimationPlayer
and then plays it. Which is an opportunity to introduce randomness to the AnimationPlayer
, and also you would be setting up all the particles from code (which you may or may not find easier to do).
I said MultiMesh
and MultiMeshInstance
being nodes is overhead. You may have a chance at cutting that overhead by talking to the VisualServer
directly. In fact, this is the approach some bullet hell games has taken due to performance (plus talking to the physics server). So there is still a chance this is the most performant way to do it, but you really got to put the effort.