Move/Copy a node from one scene(file) to another

Godot Version

v4.1.2.stable.mono.official [399c9dc39]

Question

It’s tool script.

	void TestAddToNode()
	{
		GD.Print("1");
		// Test Case 1: Adding a valid .obj file to a node
		Node scene = LoadSceneInstance("res://TestStuff/TestScene.tscn");
		Node childNode = scene.FindChild("TestNode1", true, false);
		GD.Print("2");
		Node scene2 = LoadSceneInstance("res://TestStuff/TestScene2.tscn");
		Node childNode2 = scene2.FindChild("TestNode2", true, false);

		GD.Print($"(TestAddMeshToNode) scene: {scene} childNode: {childNode} scene2: {scene2} childNode2: {childNode2}");

		// Call the method to add an empty MeshInstance3D
		//AddMesh3DToNode(parentNode);
		AddNodeToNodeInSceneFile(childNode, childNode2.Name, "res://TestStuff/TestScene2.tscn");
		
		//AddMeshToNode(objPath, parentNode);
	}
public void AddNodeToNodeInSceneFile(Node childNode, string parentNode, string scenePath)
	{
		GD.Print("3");
		if (childNode == null || parentNode == null)
		{
			GD.PrintErr($"(AddNodeToScene) Node {childNode} is null.");
			return;
		}

		if (scenePath == null)
		{
			GD.PrintErr($"(AddNodeToScene) Scene path {scenePath} is null.");
			return;
		}
		GD.Print("4");
		var packedScene = GD.Load<PackedScene>(scenePath);
		if (packedScene != null)
		{
			//create scene file instance
			var sceneRoot = packedScene.Instantiate();
			var editorInterface = GetEditorInterface();
			GD.Print("5");
			if (sceneRoot is Node sceneNode)
			{
				var foundParentNode = sceneRoot.FindChild(parentNode, true, false);
				Node dupliChildNode = childNode.Duplicate();
				dupliChildNode.Reparent(sceneRoot, true);
				foundParentNode.AddChild(dupliChildNode, true);
				childNode.Owner = sceneRoot; //needs to be after the AddChild()
				GD.Print("6");
				packedScene.Pack(sceneRoot);
				ResourceSaver.Save(packedScene, scenePath);
				var openedScenes = editorInterface.GetOpenScenes();
				if (openedScenes.Contains(scenePath))
				{
					GD.Print($"AddNodeToScene) Reloading scene");
					editorInterface.ReloadSceneFromPath(scenePath);
				}
				GD.Print($"(AddNodeToScene) Node added to the scene successfully.");
			}
			else
			{
				GD.PrintErr("(AddNodeToScene) Loaded scene is not of type 'Node'.");
			}
		}
		else
		{
			GD.PrintErr("(AddNodeToScene) Failed to load the scene.");
		}
	}

console:

--- Debugging process stopped ---
  Node needs a parent to be reparented.
  Invalid owner. Owner must be an ancestor in the tree.
_Run Started: 
1
2
(TestAddMeshToNode) scene: <Node3D#4465188992609> childNode: <Node#4465205768368> scene2: <Node3D#4465256100034> childNode2: <Node#4465272878591>
3
4
5
AddNodeToScene) Reloading scene
(AddNodeToScene) Node added to the scene successfully.

Sadly the errors are moved to the top and there is no info on what produces them. I’ve tried using prints which don’t work as the errors are move to the top and breakpoints in codium but that also doesn’t work.

I have no idea why this is happening or how to fix it and the users in the godot discord didn’t either.

For those wondering why I’m trying to create a function like this, I’m trying to automate things and I need this function to replace nodes with other nodes when I want to update them with newer version in all scenes for example. I can make and test the node in a scene and then after the test I can apply it to all other scenes via a tag system or a list of node with ID.

 Node needs a parent to be reparented.
  Invalid owner. Owner must be an ancestor in the tree.

The error message you’re encountering in Godot indicates that you’re trying to reparent a node, but the new parent you’re assigning is not a valid owner. In Godot, the owner of a node must be an ancestor in the scene tree.

Does that mean I can’t copy/move a node from one scene to another because they have different ancestors or different scene trees? Or do I have two active scene trees if two tabs are open? Or is it just one active scene tree for the node that is being edited in the selected tab?

here is the snippet on gdscript moving a node to other parent:

func move_to_selected(node,old_parent):
	old_parent.remove_child(node)
	selected_node.add_child(node)
	node.set_owner(selected_node)

instead of using the .Reparent

your error is happens somewhere else before your prints, put your code
to “comment”, method calls or bodies of methods and and start looking where is error. And refactor your code.

if (packedScene == null){
GD.PrintErr("(AddNodeToScene) Failed to load the scene.");
return;
}

You will get one less inner block and one less else like you did before :wink:
I don’t think use FindChild() is good too, is better use GetNode() or GetNode<>()
specially when you trying move things betweens nodes

The error ( Invalid owner. Owner must be an ancestor in the tree.) came from: childNode.Owner = sceneRoot; had to change it to dupliChildNode.Owner = sceneRoot; So the error does not happen before the prints.

I’ve implemented that:

	public void AddNodeToNodeInSceneFile(Node childNode, string parentNode, string scenePath)
	{
		GD.Print("3");
		if (childNode == null || parentNode == null)
		{
			GD.PrintErr($"(AddNodeToScene) Node {childNode} is null.");
			return;
		}

		if (scenePath == null)
		{
			GD.PrintErr($"(AddNodeToScene) Scene path {scenePath} is null.");
			return;
		}
		GD.Print("4");
		var packedScene = GD.Load<PackedScene>(scenePath);
		
		if (packedScene == null)
		{
			GD.PrintErr("(AddNodeToScene) Failed to load the scene.");
			return;
		}

		//create scene file instance
		var sceneRoot = packedScene.Instantiate();
		var editorInterface = GetEditorInterface();
		GD.Print("5");
		if (sceneRoot is Node sceneNode)
		{
			var foundParentNode = sceneRoot.FindChild(parentNode, true, false);
			Node dupliChildNode = childNode.Duplicate();
			//dupliChildNode.Reparent(sceneRoot, true);
			childNode.GetParent().RemoveChild(dupliChildNode); //aka old_parent.remove_child(node)
			foundParentNode.AddChild(dupliChildNode, true); //aka selected_node.add_child(node)
			dupliChildNode.Owner = sceneRoot; //needs to be after the AddChild()
			GD.Print("6");
			packedScene.Pack(sceneRoot);
			ResourceSaver.Save(packedScene, scenePath);
			var openedScenes = editorInterface.GetOpenScenes();
			if (openedScenes.Contains(scenePath))
			{
				GD.Print($"AddNodeToScene) Reloading scene");
				editorInterface.ReloadSceneFromPath(scenePath);
			}
			GD.Print($"(AddNodeToScene) Node added to the scene successfully.");
		}
		else
		{
			GD.PrintErr("(AddNodeToScene) Loaded scene is not of type 'Node'.");
		}
	}

But now I get scene/main/node.cpp:1425 - Condition "p_child->data.parent != this" is true.

If I change it to:
dupliChildNode.GetParent().RemoveChild(dupliChildNode); Both have the same scene as a parent.

I get:

modules/mono/glue/runtime_interop.cpp:1324 - System.NullReferenceException: Object reference not set to an instance of an object.
     at ManifestReader2.AddNodeToNodeInSceneFile(Node childNode, String parentNode, String scenePath) in /ManifestReader2.cs:line 822
     at ManifestReader2.TestAddToNode() in /ManifestReader2.cs:line 58
     at ManifestReader2._Run() in /ManifestReader2.cs:line 26
     at Godot.EditorScript.InvokeGodotClassMethod(godot_string_name& method, NativeVariantPtrArgs args, godot_variant& ret) in /root/godot/modules/mono/glue/GodotSharp/GodotSharpEditor/Generated/GodotObjects/EditorScript.cs:line 98
     at ManifestReader2.InvokeGodotClassMethod(godot_string_name& method, NativeVariantPtrArgs args, godot_variant& ret) in /Godot.SourceGenerators/Godot.SourceGenerators.ScriptMethodsGenerator/ManifestReader2_ScriptMethods.generated.cs:line 262
     at Godot.Bridge.CSharpInstanceBridge.Call(IntPtr godotObjectGCHandle, godot_string_name* method, godot_variant** args, Int32 argCount, godot_variant_call_error* refCallError, godot_variant* ret) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs:line 24

Got it working:

	public void AddNodeToNodeInSceneFile(Node childNode, string parentNode, string scenePath1, string scenePath)
	{
		GD.Print("3");
		if (childNode == null || parentNode == null)
		{
			GD.PrintErr($"(AddNodeToScene) Node {childNode} is null.");
			return;
		}

		if (scenePath == null)
		{
			GD.PrintErr($"(AddNodeToScene) Scene path {scenePath} is null.");
			return;
		}
		GD.Print("4");
		var packedScene = GD.Load<PackedScene>(scenePath);
		
		if (packedScene == null)
		{
			GD.PrintErr("(AddNodeToScene) Failed to load the scene.");
			return;
		}

		//create scene file instance
		var sceneRoot = packedScene.Instantiate();
		var editorInterface = GetEditorInterface();
		GD.Print("5");
		if (sceneRoot is Node sceneNode)
		{
			var foundParentNode = sceneRoot.FindChild(parentNode, true, false);
			Node dupliChildNode = childNode.Duplicate();
			//dupliChildNode.Reparent(sceneRoot, true);
			//dupliChildNode.GetParent().RemoveChild(dupliChildNode); //aka old_parent.remove_child(node)
			foundParentNode.AddChild(dupliChildNode, true); //aka selected_node.add_child(node)
			dupliChildNode.Owner = sceneRoot; //needs to be after the AddChild()
			GD.Print("6");
			packedScene.Pack(sceneRoot);
			ResourceSaver.Save(packedScene, scenePath);
			var openedScenes = editorInterface.GetOpenScenes();
			if (openedScenes.Contains(scenePath))
			{
				GD.Print($"AddNodeToScene) Reloading scene");
				editorInterface.ReloadSceneFromPath(scenePath);
			}
			GD.Print($"(AddNodeToScene) Node added to the scene successfully.");
		}
		else
		{
			GD.PrintErr("(AddNodeToScene) Loaded scene is not of type 'Node'.");
		}
	}

It looks like when a node is duplicated it has no owner so dupliChildNode.GetParent().RemoveChild(dupliChildNode);
Did output these errors.

Thank you for the pointers!

2 Likes

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