I am trying to remove a child node from a parent node, because I want to disconnect it without releasing it from memory. I would then like to reattach this node to the scene tree, to the same parent again.
Wider context:
I have a main menu that, when a “start game” button is pressed, it will fade out and then be removed from the scene tree. When the player presses the escape key however, I would like this main menu to be reattached again, continuing to update only after it has been reattached.
Here is a boiled down example of my code;
public partial class MainMenu : Control
{
private bool fadingOut;
private Node child;
public void _Ready()
{
child = GetChild(0);
}
public void _Process(double delta)
{
if (Input.IsKeyPressed(Key.Escape))
{
this.AddChild(child);
}
}
// Called from elsewhere
public void FadeOut()
{
// **some control node modulate stuff to do the fade**
this.RemoveChild(child);
}
}
Upon pressing the escape key, it will give the error "Can’t add child ‘childNode’ to ‘MainMenuNode’, already has a parent 'MainMenuNode’.
I have also tried using reparent instead as shown below…
child.Reparent(this);
But again upon pressing escape, it gives the error “Node needs a parent to be reparented”
My questions are these;
How can the node think that is has a parent and also no parent at the same time, which the two errors seem to suggest.
What is the correct general approach here? The concept of keeping the main menu in memory but not updating it is appealing to me and my use case, but its unclear how to actually reattach something once its been removed
_Process runs once each frame and every second has (in default settings) roughly 60 frames.
So in the first frame, the key is pressed and the child is added.
In the second frame, the key is still pressed and you try to add the child again.
Regarding the alternative with reparent:
In the first frame the child has no parent and reparent fails because of that.
So the two error messages happen in different frames for different reasons.
Just for my own ease, I created an extension method so I dont have to rewrite this everytime.
public static void AddChildSafe(this Node parentNode, Node childNode)
{
if (childNode.GetParent() != null)
{
childNode.GetParent().RemoveChild(childNode);
}
parentNode.AddChild(childNode);
}
Note: This can be called using the same syntax as AddChild() can be, but you might need to write this.AddChildSafe() if you are calling it on the same node as your script (if anyone can explain why, fire away in the comments). For example;
void MyMethod()
{
AddChild(childNode); // AddChild can be called directly with no issues
this.AddChildSafe(childNode); // AddChildSafe extension method requires a this. prefix.
}
Hope this helps anyone in future!!
Edit 1: as @Sauermann pointed out, I also shouldnt be doing AddChild() nor Reparent() within the _Process() function.
Excellent point, you’re right that it shouldn’t be done in process like that. Ive added a comment with the solution that Ive found below, but this was also part of the problem. Thanks!!
I’m doing something like this (a pause menu in my game), but I’m leaving it in the tree and just controlling visibility with .show() and .hide(). I suppress updates by wrapping the important bits of the linked scripts with if is_visible_in_tree():, so updates only happen when it can be seen.