I needed to create a class with a function that creates a node and I had to spend a lot of time to understand that get_node("node_name") does not work the same as GameObject.Find("name") in Unity. In general, as I understand it, the script can search for nodes only if it โ€œexistsโ€ inside the scene, i.e. assigned to a node. But classes don't exist anywhere at all except in the game files, so it's impossible to create my class (probably).

Does anyone have any ideas on how to solve this problem?

Excerpts from scripts:
Class:

...
CORE = get_node("core_node")
...
func InitLabel(init: String):
var label = Label.new()
...
CORE.add_child(label)

A script that exists in the scene and calls a function:

var testcfg = CScriptIFInit.new()
testcfg.ParseFile("ui_datatest.cfg")
testcfg.InitLabel("testlabel")

Error: Attempt to call function 'add_child' in base 'null instance' on a null instance.

  • var CORE = get_node(".")
    ...
    CORE.add_child(testcfg)

    This would be equivalent:

    var CORE = self
    ...
    CORE.add_child(testcfg)

    Or more simply:

    add_child(testcfg)

    I suspect the problem is that the code is being executed before the scene tree has been initialized. The code should be executed from a _ready() method, and/or executed using call_deferred().

CORE = get_node("core_node")

Is that script attached to a node that has a child node named "core_node"? If not, it will set CORE to null.

    DaveTheCoder Of course not, it's a class and it's not attached to any node. This is the problem, is there a way around this?

    • xyz replied to this.

      There are various ways to reference a node, such as absolute node path from the scene root, unique scene node name, exported node path, signal that passes a node reference.

      Can you explain more about how the class will be used?

      When the code is executed, it will probably be called, directly or indirectly, from a script that's attached to a node that's in the scene tree.

      An exception could be if it's executed by an event such as a timer or input. I'm not sure if there would be an associated node in those cases.

        kSarkans You need to instantiate an object of a class if you want to set its properties and call its methods. Unless you make those properties and methods static. Then they are not associated with any specific instance of that class (i.e. they are unique to whole class) and can be accessed via class namespace.

        DaveTheCoder We create a script, for example "test.gd" and attach it to any node.

        We write in it:

        var testcfg = CScriptIFInit.new() # Creating a var
        testcfg.ParseFile("ui_datatest.cfg") # parsing config file
        testcfg.InitLabel("testlabel") # creating a Label

        CScriptIFInit is a class declared in CScriptIFInit.gd, which contains a set of commands that creates a Label node (testlabel) and takes its settings from the config file (ui_datatest.cfg).

        I just tried to create an additional node on which I applied a class script and made it local. Didn't work.
        Another option didnโ€™t work either - in the script from where the class functions are called, I did something like this:

        var CORE = get_node(".") # now CORE is the node to which the script is assigned
        var testcfg = CScriptIFInit.new()
        CORE.add_child(testcfg) # and I tried to create my class as a node
        testcfg.ParseFile("ui_datatest.cfg")
        testcfg.InitLabel("testlabel")

        Didn't worked. Or it works, but the node is not created

        • xyz replied to this.

          kSarkans Didn't worked

          This is not how coders should talk ๐Ÿ˜‰ We need exact errors.

          Let's see your class definition code. In order for class to function as a node it, obviously, must inherit from built-in base class Node or any other class that inherits Node. Otherwise there will be no node functionality.

            var CORE = get_node(".")
            ...
            CORE.add_child(testcfg)

            This would be equivalent:

            var CORE = self
            ...
            CORE.add_child(testcfg)

            Or more simply:

            add_child(testcfg)

            I suspect the problem is that the code is being executed before the scene tree has been initialized. The code should be executed from a _ready() method, and/or executed using call_deferred().

              xyz

              xyz This is not how coders should talk ๐Ÿ˜‰ We need exact errors.

              I'm trying to find an error at random, because debugger messages do not always give a complete picture, especially when nothing works, but there are no errors.

              CScriptIFInit.gd (after my experiments, garbage accumulated in the code)

              class_name CScriptIFInit extends Node
              
              
              var ParsedFile
              var CNODE:Node
              const IF_PATH = "res://gamedata/config/ui/"
              
              
              func _init(node_root: String):
              	
              	CNODE = get_node(node_root)

              Parses config file

              func ParseFile(file: String):

              ParsedFile = ConfigFile.new()
              ParsedFile.load(IF_PATH + file)
              if ParsedFile == null:
              	DisplayServer.clipboard_set("ERROR (0): File "+file+" not found.")
              	push_error("ERROR (0): File "+file+" not found.")
              else:
              	print("File "+file+" is loaded.")

              Initializes a new label

              func InitLabel(init: String):

              var label = Label.new()
              var ls = LabelSettings.new()
              label.name = init
              
              # Setting up a label
              if ParsedFile.get_value(init, "text") == null:
              	DisplayServer.clipboard_set("ERROR (1): Important attributes not found. Attribute: ["+init+"]: text.")
              	push_error("ERROR (1): Important attributes not found. Attribute: ["+init+"]: text.")
              else:
              	label.text = ParsedFile.get_value(init, "text")
              if ParsedFile.get_value(init, "x") == null:
              	DisplayServer.clipboard_set("ERROR (1): Important attributes not found. Attribute: ["+init+"]: x.")
              	push_error("ERROR (1): Important attributes not found. Attribute: ["+init+"]: x.")
              else:
              	label.position.x = ParsedFile.get_value(init, "x")
              if ParsedFile.get_value(init, "x") == null:
              	DisplayServer.clipboard_set("ERROR (1): Important attributes not found. Attribute: ["+init+"]: y.")
              	push_error("ERROR (1): Important attributes not found. Attribute: ["+init+"]: y.")
              else:
              	label.position.x = ParsedFile.get_value(init, "y")
              if ParsedFile.get_value(init, "font_size") == null:
              	ls.font_size = 12
              else:
              	ls.font_size = ParsedFile.get_value(init, "font_size")
              
              ls.font_color = Color.CHARTREUSE # test
              label.label_settings = ls
              
              print(label.text) # test
              CNODE.add_child(label) # init

              and test.gd

              extends Node
              
              func _ready():
              	var testcfg = CScriptIFInit.new(".") # sets CNODE to node to which this script is attached
              	testcfg.ParseFile("ui_datatest.cfg")
              	testcfg.InitLabel("testlabel")

              DaveTheCoder add_child(testcfg)

              mf FINALLY WORKS! I've already done this, but in a different order. ๐Ÿ˜†
              Big thanks!