3d – which is more performant particles_system or animation_player

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:

  1. It is not made for randomness. Every time the animation plays, it is the same.
  2. 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.