How do you load particles so that they don't lag at the start of a game?

Godot Version

4.2.1

Question

I’m making a game that utilizes particle effects and whenever they are played for the first time, the game stutters until it loads the effect.
I’ve tried to look up how to handle this but there does not seem to be a good solution. One is to play all the particles on the scene start. This is not desirable because it causes a bunch of upfront lag. The second way I would think is to preload the particles. But when I try to use resource loader and hide it all behind a loading screen, it doesn’t seem to work either.
What’s the proper way to load particles behind a loading screen?

Prefer preload, over load, it should not stutter. And is also recommended by the docs for resources with static file paths. Use load for dynamic resources.

Thanks pennyloafers but I always use Preload and this does not seem to help :frowning:

Try using a background loader thread to load and instantiate the particle effect. It will introduce a small lag between when it is needed and when it appears, but the lag might be acceptably small and a reasonable tradeoff to eliminate the stutter.

In a game I’m making, I dynamically load the parts of a map. Some of those parts are large, and made the loading screen jerk and stutter a LOT. I rewrote the loading class to use a background loader, and all the jerking and stuttering went away.

Thanks Soaps. Do you think you can provide a link or example? When I some research on background loading I get the Resource Loader docs which didn’t end up working for me. Appreciate the help!

Here’s the core of my background loader:

func _ready():
	m_nError = ResourceLoader.load_threaded_request(m_stPath)
	if m_nError != OK:
		print("Can't start the threaded load.")

func _process(delta):
	if !m_bDone:
		m_nError = ResourceLoader.load_threaded_get_status(m_stPath)
		match m_nError:
			ResourceLoader.THREAD_LOAD_INVALID_RESOURCE:
				fatalError.emit(self)
				queue_free()
			ResourceLoader.THREAD_LOAD_IN_PROGRESS:
				pass
			ResourceLoader.THREAD_LOAD_FAILED:
				fatalError.emit(self)
				queue_free()
			ResourceLoader.THREAD_LOAD_LOADED:
				var scene = ResourceLoader.load_threaded_get(m_stPath).instantiate()
				
				m_bDone = true
				loaded.emit(self,scene)
				queue_free()
			_:
				fatalError.emit(self)
				queue_free()

The rest is just support stuff, like game state variables, accessors, and signals that are specific to my games.