+3 votes

Hi,
I really hope someone can help. This problem is completely show-stopping for me.

I'm creating nodes with new(), then adding them to the hierarchy with addchild(), then using setowner() after that.
However, the nodes never show up in the editor (hierarchy), no matter what I do.
I know that they are being created, because by using printtreepretty(), it shows them there.

Also, the created nodes are destroyed after the game is closed. They don't stay in the hierarchy.

Here's the link to download the example project so you can see for yourself:
https://www.dropbox.com/s/hjufss85461tanq/Add%20Child%20Example.zip?dl=0

(Press the "Enter" key to run the function that creates child nodes, then read the output for information. It will create a child every time you press "Enter".)

If you don't want to download that file, here's the code from the project.
(Sorry for the formatting.)

The hierarchy Looks likes this:
--Main
----Holder

[Here's the Main node's script:]

tool
extends Node


var child_node : Node
const child_name : String = "Child"
var child_script : Script = preload("res://child_script.gd")



var holder_node : Node
const holder_name : String = "Holder"


func add_child_to_holder_node():
    child_node = Node.new()
    child_node.name = child_name

    holder_node.add_child(child_node)
    child_node.set_owner(self)
    child_node.set_script(child_script)

    print_tree_pretty()




func _ready():
    holder_node = find_node(holder_name)
    pass 



func _input(event):
    if event.is_action_pressed("ui_accept"):
        add_child_to_holder_node()

[Here's the child_node's script:]

extends Node

var number_of_child_nodes : int


func _init():
    number_of_child_nodes = get_parent().get_child_count()

    print(" ")
    print("child_script was successfully attached to created child_node.")
    print("number_of_child_nodes = " + str(number_of_child_nodes))
    print(" ")
in Engine by (23 points)
edited by

Ok, I think I understand now. You want to add and remove nodes while the game runs (no problem) and to make this persistent (so save the scene during runtime)? (a bit of a problem)

This is a completely different story.
You can change scenes in the editor. Manually, or by tool scripts or by plugins/addons.
Then save the scene in the editor.

You normally can't save changed scenes during runtime. Scenes are compiled before running and also often in some package. Also, scenes are usually in the resource (res://) path which is or can be read only on many platforms.

So if you want to add nodes to a scene while the game runs and make this persistent, the easiest way is to keep track of this in a data structure (typically a dictionary). Save this dictionary to i.e. a JSON file before the game ends and restore it either when the game runs the next time. You could probably also use a tool script to read and apply that file permanently with a tool script in the editor. (Just have to check that the user:// path where you would change such file normally is physically the same during runtime and in editor)

Maybe there also exist tools which read and write parts or entire scene trees from/to files or strings during runtime.

Yes, I'm trying to have those changes be saved while the game is running.
So basically, it's not possible.

I tested your child creation/deletion script, and it works great, but the user won't have access that export variable toggle.

I tried to call that setter function manually upon a button press, and it simply does the same thing as it was doing before.

I'll have to look into writing to and reading from files. I was afraid I would have to do something like that.

But It probably wouldn't be efficient to write to file/reload scene/read from file every time I want if I want to be creating/destroying things.

I'll have to rethink how to structure my project then.

Thanks a lot for your help.

Basically, reading and writing files isn't very hard. And while you advance through programming knowledge you should master this. :-)

And, it's also in the "Fine Manual" (docs):
http://docs.godotengine.org/en/3.0/tutorials/io/saving_games.html

Just be aware that there might be differences in the user:// path (maybe also res://) between runtime and editor.

But what you want to do isn't very typical for a game. That leads to the question if your basic approach is really the right way to go.

In your scenario the game user would have to have access to the whole project source files and the godot editor. This is less like a game and more like a game designer tool.

This would IMHO make more sense to run entirely in the editor. Then the right approach would be to make this an addon.

Thanks again for your help and advice!

Someone pointed out to me that while the game is running, I can switch from Local debugging mode to Remote debugging mode.

I never noticed that option before, and remote debugging shows me all the items I create dynamically, which is great.

I wanted to be able to see if I was creating/destroying objects correctly, and Remote debugging allows me to do that.

Next I'll work on getting saving and loading to work.
Thanks!

1 Answer

+2 votes
Best answer

You should try to set the scene root as owner.

    fence = MeshInstance.new() #create new fence
    fence.set_name(MESHNAME)
    add_child(fence)
    fence.set_owner(owner) 

The code above is a working example (part of a "fence" generating script).
The parents owner is also the scene node so it can be used as a convenient shortcut.

by (3,214 points)
selected by

Hi, Wombat, thank you for your quick response.

I changed your code a bit to this:

func _ready():
    holder_node = find_node("holder")

    thing = Node.new()
    thing.name = "test thing"
    holder_node.add_child(thing)
    thing.set_owner(self)

That code works when it is placed in the _ready() function, but when I paste that same exact code in an input event or button press event, it doesn't work.
The problem persists no matter whether I'm adding the child to the root node or to a different node.

I need to be able to add children after a certain event takes place, not in the ready() function.

Do you happen to know why it doesn't work anywhere else in the script?

Thank you.

You use self in the set_owner. This will not always get the scene node.
Why not?:
thing.set_owner(holder_node.owner)
(Edited to correct a typo)

That sets the new nodes owner to the same as the holder nodes owner (which should already be the scene node).

Check also the output window for errors. Sometimes you need to use a call_deferred when editing the scene node inside signals handling.

Thanks for your continued help. However, it's still not working.

I tried setting the owner of the created_child (node created with new() function ) in every conceivable way.
I set the root node (Main) to a variable, and tried it the following ways:

  1. createdchild.setowner(main_node)
  2. createdchild.setowner(holdernode.getparent())
  3. createdchild.setowner(holder_node.owner)
  4. createdchild.setowner(gettree().getroot().get_child[0])
  5. createdchild.setowner(get_node("/root/Main"))
  6. I tried call_deferred().
  7. I tried setting up a factory node that adds the child where I want it in it's ready() function when the factory is created, then I used queuefree() to delete the factory after the child was created.

Every result was the same.
No error message, no warning.
The createdchild nodes show up when I use printtree_pretty(), but they don't show in the hierarchy, so I can't actually do anything with them.
Also, they get destroyed upon exiting the game.

I have no idea what else to try at this point.
If this isn't a bug, then I'm seriously missing something here.
Creating nodes should not be this difficult.

Ok, I made an example.
Set up a scene (i used one with a control as root, but doesn't matter much).

Add a button (you could use basically any node but I extended this script from button).

Add this script: (can be embedded)

tool
extends Button

export(bool) var haschild = false setget haschild_set

const CHILD_NAME = "child"

func _ready():
    haschild = has_node(CHILD_NAME)
    print("toolscript ready: haschild: "+str(haschild))

func haschild_set(newval):
    print("set haschild: "+str(newval))
    haschild = newval
    if !haschild and has_node(CHILD_NAME):
        var c=get_node(CHILD_NAME)
        remove_child(c)
        c.queue_free()
        print("child removed")
    elif haschild and !has_node(CHILD_NAME):
        var c=Control.new()
        c.name=CHILD_NAME
        add_child(c)
        c.owner = owner
        print("child '"+CHILD_NAME+"'added")

Important: Save and reload the scene. You probably have to do this every time after you changed the script.

Then (in the editor) click on the button to make it active and toggle the (new) "Haschild" property (in the properties Inspector). This should directly add or remove a childnode. I tested this successfully here in godot 3.1.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.

Categories