Managing Particles

Godot Version

4.5

Question

I’m wondering if anyone has had some success at advance handling particles in an abstraction that makes sense?

I’m looking for opinions on particle management. Pros and cons of setups.

requirements

  • reusable root game object script (e.g. player, enemy, obstacle, etc.)
  • expandable particle events for similar object classes, to maintain a level of artistry
  • particle independence from game object.
  • minimal code within game object. (will indicate a particle event, but does not care about how its managed )
  • avoids inheritance, abstract classes are okay.
  • performance: system pooling and reusable resources (this is secondary concern, but would be nice-to-have)

I have a reusable spaceship class script that will have various instances for the player and enemies. It is tied a scene that has some thrust effects currently. I now need to add some other effects like explosions and collision effects.

I could place particle systems in the spaceship scene and control events in the root script, but if i decide to use some special particles for one spaceship implementation it would mean diluting all instances with this one spaceship’s particle considerations into the same script.

finite particle set

To combat this I could abstract/categorize events into a finite set. ( death, collision, boost, attack, special, etc. ). But this to me seems limiting in freedom for object design.

detached particles

Another considerations, is that I will be adding and removing spaceships within the scene
but I would like particles to exist independently within the scene. I in some cases i could simply hide and deactivate parts of the ship class until the particles are finished before a queue free, but I feel like this will bloat the spaceship script with a bunch of asynchronous condition handling. One thing that could be alluring here is some resource optimizations and pooling of systems that can be reused.

For this, the obvious solution is to introduce a manager system to observe the ship for events and handle the particles, but I don’t like the idea of having to make such an abstraction to setup and listen for events from the spaceship class to handle the particles. In this case any expansion of particles would be lumped here instead the the game object script. (this doesn’t sound ideal but its a trade off for simpler dependent scripts )

particles in animations

I don’t have any of these yet, but I don’t know how well a manager system would work with an animation system for particular particles events. it would seem that particles would need to exist for previewing in the animation. so this would be a knock against the manager approach.

final thoughts

It seems like keeping the particles within the scene, for animations and convenience seems to be the best approach. Stomach the asynchronous handling within the script and potentially duplicate the script for potential edge case versions of enemy spaceships…

I have done some research for this but google conflates this with many entry level tutorials on particle systems, and not their management… What have you done to solve this issue? Do i just need to accept some level of duplication to maintain artistic freedom?

In Stahldrache I have several particle effects (missile trails, engine smoke when crashing…). The way I made them work was to make them their own (single node) scenes, and attach them at runtime if needed. The missile init code, for instance, checks if missile trails are enabled, and if so, instantiates a contrail particle system and add_child()s it to the missile.

1 Like

Don’t do that. Design from specific to general, not the other way around.

1 Like

Two ideas come to mind: composition and Resource objects. Composition-wise, do something like @hexgrid suggested. Add nodes that are configured beforehand.

Resource-wise I’m thinking you create a Resource object that you attach to a game object (ship, etc.). When called, it reads all the values set then constructs a ParticleGPU2D/3D object, applies all the modifiers, and spawns the object as needed.

1 Like

I think thats good advice, but not all variables are known and im taking an additive approach to design, stating with a basic skeleton and then flesh it out. (Which is maybe bad starter?)

There are some opinions of design where you should generalize as much as possible. e.g. everything in the linux OS is a file.

I could see this approach like Casey Muratori’s semantic compression, where i start with concrete, unoptimized things until i have a better picture and a working solution before making abstractions.

Could you clarify your meaning?

1 Like

I think thats more or less what im doing now, just looking for a better way if it exists.

1 Like

Definitely listen to Casey.

The problem is, when you’re within an object oriented game engine, you’re forced to inherit from its classes to build your game objects. However engine classes are technical and your game objects follow your game semantics. Those two may not have compatible class hierarchies. Various types of your entities may require completely different technical classes for implementation within the engine. That’s why it’s not a good idea to start with something like Entity in a Godot game. Which engine class should it inherit? Unless it inherits nothing and your hierarchy is completely decoupled from its representations in the engine. Your game class hierarchy doesn’t start from scratch so you don’t have the luxury of using “everything is a file” approach.

1 Like

:thinking:

I think this is an interesting idea, i havent put a lot of thought into composition, mostly because i was hoping to avoid some sort of factory pattern until necessary or having to think in a fragmented way, but if the scene is not dynamically built i could see that being a potential path forward. I do expect to use scenes to differentiate the variations of game objects, just dont want to duplicate code in multiple scripts.

1 Like

I use a very simple script that will not be useful for someone that needs it to handle particles for a lot of different use cases. I just use it for single use particles emitted when something e.g. dies, tackles into something or whatever. It would be easy to modify though!

It only has one function:

func emit(particle_name: String):
	if particles_set.has(particle_name):
		var particle = particles_set[particle_name]
		var particles_instance = particle.instantiate()
		add_child(particles_instance)
		particles_instance.color = modulate
		particles_instance.position = particles_offset[particle_name]
		particles_instance.emitting = true
	else:
		push_error("%s not found" % particle_name)

Two dictionaries: one for the particles, found by string keys, one for the offsets, also found by string keys. So if i want one to spawn a bit higher up or further away from whatever calls the manager node, the offset will automatically be right without having to send in additional arguments or such. Good to have a push error else at the end since some of these string lookups can fail without giving an error if there is no matching string. Not sure if this is the case here but it doesn’t hurt to include it! Just a good way to avoid having to look look for bugs when the problem is just a typo somewhere in a string.

1 Like

So it sounds like approaches @hexgrid @dragonforge-dev and @baba will eventually be what i do, but im going to take @normalized words as a mantra to step back from preemptively abstracting something yet.

So i will leave this with a quote from Bruce Lee and wait until i have a better picture for what i need because I still need to make the cup to put the juice in.

Be like water making its way through cracks. Do not be assertive, but adjust to the object, and you shall find a way around or through it. If nothing within you stays rigid, outward things will disclose themselves.

Empty your mind, be formless. Shapeless, like water. If you put water into a cup, it becomes the cup. You put water into a bottle and it becomes the bottle. You put it in a teapot, it becomes the teapot. Now, water can flow or it can crash. Be water, my friend.
Bruce Lee

Thank you everyone!

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.