Search code examples
godotgdscriptonready

I am getting an error "Invalid call. Nonexistent function 'set' in base 'Nil'."


I am new to Godot. Trying to make a counter for some values of reactor core. Here is my code:

extends MarginContainer

export var obj=""
export var expr_list={"sig":""}
export(int) var value_from_model setget set_val
onready var panel_frame=get_node("frame")
onready var panel=get_node("frame/Panel")

func set_val(val):
    value_from_model=val
    if panel_frame != null:
        panel_frame.margin_right=value_from_model
    check_signal()
    
func check_signal():
    var new_style = StyleBoxFlat.new()
    var for_panel = StyleBoxFlat.new()
    #var panel_frame=get_node("frame")
    new_style.border_width_top=2
    new_style.border_width_bottom = 2
    new_style.border_width_left=2
    new_style.border_width_right=2
    new_style.border_color="#1bf408"
    new_style.bg_color='#0b0e0e'
    panel_frame.set('custom_styles/panel', new_style)
    for_panel.bg_color="#1bf408"
    panel.set('custom_styles/panel', for_panel)
    
    
func _ready():
    check_signal()
    set_val(value_from_model)
    
    

First my onready variables panel_frame and frame were in the funtion check_signal() as local variables. But I was getting an error "get_path: Cannot get path of node as it is not in a scene tree." , "get_node: (Node not found: "frame/Panel" (relative to "").) ". After I made them global and changed to onready var. And now I am getting this error.

When I try to change vavalue_from_model in the inspector, the error appears again

Here is my progress bar which has to be changing every time when I change the value_from_model


Solution

  • If get_node is failing, the node isn't there… So it gives you an error. That also explains why set fails: the variable that should have been initialized with get_node wasn't initialized.

    Usually, that would be a matter of fixing a wrong path. You could even have Godot generate the correct path for you, by dragging the node over the script.


    However, since get_path was also failing, I suspect you are facing a slightly more complex situation.

    I believe you or Godot are setting value_from_model as part of the instantiation process, which triggers set_val, before the ready mechanism.

    You can check with is_inside_tree. The pattern I often use is as follows:

    func set_val(val):
        if value_from_model==val:
            return
    
        value_from_model=val
        if is_inside_tree():
            update_val()
    
    
    func _enter_tree() -> void:
        update_val()
    
    
    func update_val() -> void:
       if panel_frame != null:
           panel_frame.margin_right=value_from_model
    
       check_signal() 
    

    I'm going to suggest to modify it to use _ready instead of _enter_tree, since you need children nodes, and _ready will run after the children are ready. However, remember that _ready will only run once, unless you call request_ready, which might be important if you are going to remove and add the node multiple times during runtime.

    You could also use yield to wait until the node is ready or inside the scene tree, however, I have moved away from yield, that is another tale.

    Similarly, you should not need to do set_val(value_from_model) in _ready. Furthermore, you might even do panel_frame.margin_right=value_from_model inside of check_signal, and then you won't need two different methods (check_signal and update_val).

    So we land into this:

    func set_val(val):
        if value_from_model==val:
            return
    
        value_from_model=val
        if is_node_ready():
            check_signal()
    
    
    func _ready() -> void:
        check_signal()
    
    
    func _exit_tree() -> void:
        request_ready()
    
    
    func check_signal() -> void:
       if panel_frame != null:
           panel_frame.margin_right=value_from_model
    
       # rest of the code