Search code examples
godotgdscript

Access var from another script in Godot


Node World contains two children- Pe2node and HUD. Pe2node (Node2D) node has attached pe2.gd script and it has variable - shift. HUD node (CanvasLayer) has attached HUD.gd script and I want display variable shift from Pe2node. I try some variants, but it don't work at all. I have ready to try autoload but may be there is simple way to get it work. HUD.gd:

extends CanvasLayer

var fps=0
var shift_hud=0

func _process(delta):
    fps = Engine.get_frames_per_second()  
    $var_fps.text=str(fps)
    #shift_hud=get_node("Pe2node").get_variable("shift")
    #shift_hud=get_node("Pe2node").shift
    #shift_hud=get_node("World/Pe2node").shift
    #shift_hud=$World/Pe2node.shift
    $var_shift.text=str(shift_hud)

Solution

  • Node World contain 2 children - Pe2node and HUD

    This means that Pe2node and HUD are siblings, right?

    Let us go over you attempts:

    1. get_node("Pe2node")
      

      This is looking for a "Pe2node" child of the current node, not a sibling.

    2. get_node("World/Pe2node")
      

      And now you are looking for a "Pe2node" child of "World" child of the current node.

    3. $World/Pe2node
      

      Same as above.

    You can either go one level up, like this:

    get_node("../Pe2node")
    

    Or like this:

    $"../Pe2node"
    

    Or you could have an absolute path, that I will not put example of, but if you print the result of calling get_path on the Node you want, it will show the absolute path (it starts with "/root/").


    The reason why I often do not advice doing it the way I describe above is because it depends on the relative position of these nodes, which you might not move together, breaking the code.

    A better way to do it - without going for the autoload solution - is to export a NodePath:

    export var node_path:NodePath
    

    Which you can set to the node you want in the inspector panel. And then we use it to get the node:

    get_node(node_path)
    

    This will work as long as the things you are connecting are in the scene tree from the start (you are not trying to get something instanced dynamically). And as long as that is the case, and you only manipulate the scene from the Godot interface, Godot can keep the NodePath updated.


    As per the autoload solution, I would suggest to put signals there instead. You can have an autoload with just a signal like this:

    extends Node
    
    signal shift_changed(value)
    

    That would be the whole code of the autoload.

    You can create that script and then add it as an autoload in the project settings, with some name. I'll be using the name SignalBus.

    And then from anywhere emit the signal:

    SignalBus.emit_signal("shift_changed", value)
    

    And from anywhere connect to it:

    SignalBus.connect("shift_changed", self, "_on_shift_changed")
    

    Which assumes a method something like this:

    func _on_shift_changed(value) -> void:
        pass
    

    Which means that you no longer need any kind of node path at all, so there is no path that could break. Plus you can update the UI only when the value changes, instead of having it checking all the time. And there might not be something at the other side, so this works for things that are instanced dynamically too.


    To be fair, you could put the variables on the autoload instead of the signals. That also works. And in some cases it is useful to put a reference to a Node there.

    The advantage of placing the variables there is that it also gives you a single place to place them all, which might also be useful for a save/load feature, and also to keep them around when going from one scene to another.