Best way to make effect attached to node survive the node's death.

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By hanke

Hi all

I have a smoke trail GPU2DParticles effect attached to a rocket. If the rocket explodes and is removed from the tree, I’d like the effect to be reparented to another node (root in the example), so that the trail remains and does not immediately disappear with the rocket.

I used to do this explicitly (reparent on collision → explosion), but I am wondering if this could be done on e.g. tree_exiting or another signal or method call on a script on the effect itself.

extends GPUParticles2D

func _ready() -> void:
    connect("tree_exiting", trigger_reparent)

func trigger_reparent() -> void:
    # Silently does not work.
    # call_deferred("reparent")

    # Error: trigger_reparent: Parent node is busy setting up children.
	    reparent()

func reparent() -> void:
    var gp = global_position
    self.get_parent().remove_child(self)
    self.get_tree().root.add_child(self)
    self.global_position = gp

If I immediately try to reparent in tree_exiting, I get the cryptic “Parent node is busy setting up children.” (I assume the parent node state is just not ok for removing children between tree_exiting and tree_exited)

What is the best way to reparent e.g. effects on parent death? Thanks!

:bust_in_silhouette: Reply From: bingbong

The effect could be parented to the main scene this way I think.

Sending the effect to a child of the main scene on a signal, rather than to the rocket itself, and having the main scene recieve the particles instead. The particle node would need to be its own scene

-something like this in the particles script-
func start_particle_emit(…)
emit_signal('smoke…)

-something like this added to main scene script-
func _on_smoke_emit()
add_child(smoke)
smoke.start

That way although the rocket made the smoke start, it is never part of the rocket, so once thats exploded you should still have the smoke.

you might need to figure out additional code how the main scene particles position follows the rocket once its moving, but thats probably fairly simple!

Thanks for your answer!

I’ve now set it up as follows:

Engine > RemoveTransform2D > Effect

And the Smoke effect has a script to detach and set itself as the target of the RemoteTransform2D:

extends GPUParticles2D

func _ready() -> void:
  	var parent = self.get_parent()
  	if parent.is_class("RemoteTransform2D"):
  		# "Attach" to old parent.
  		reparent.call_deferred(parent)

func reparent(parent) -> void:
  	# Reparent.
  	var gp = global_position
  	var root = get_tree().root
  	parent.remove_child(self)
  	root.add_child(self)
  	self.global_position = gp
  	# Point RemoteTransform2D to this effect.
  	parent.remote_path = get_path()

And then, when the rocket Engine goes away, I’ll stop the effect by getting the effect via its remote_path.

Works fine so far!

hanke | 2022-04-04 07:52

Yay, glad it worked, I forgot the RemoteTransform node even existed, so I guess that makes things even simpler!

bingbong | 2022-04-07 19:54

It does! Still, handling effects correctly via reparenting etc is a surprisingly manual affair in Godot.

hanke | 2022-04-08 21:37