0 votes

I know Resource has changed signal (which seems to be buggy in custom resources), but I am interested in prop change detection in Node. I tried overriding _set, but that didn't pan out (I have to provide a return value signalling whether property exists, to add to the fire there is also no _set in the parent class, so I would have to somewhere store all property names which defeats the point).

Real world example:

export(SpawnShape) var shape_type:= SpawnShape.POINT
export var shape_rectangle_size:= Vector2(1, 1)
export var shape_circle_radius:= 1.0
export var shape_ellipse_radius:= Vector2(2, 1)

among other properties and I want to detect any change (in this specific case from inspector) in any property which name is starting with shape_.

This "solution" is verbose, mind-numbing, prone to bugs and doesn't scale well:

export(SpawnShape) var shape_type:= SpawnShape.POINT setget _set_shape_type
export var shape_rectangle_size:= Vector2(1, 1) setget _set_shape_rectangle_size
export var shape_circle_radius:= 1.0 setget _set_shape_circle_radius
export var shape_ellipse_radius:= Vector2(2, 1) setget _set_shape_ellipse_radius

func _set_shape_type(x):
    shape_type = x
    update()

func _set_shape_rectangle_size(x):
    shape_rectangle_size = x
    update()

func _set_shape_circle_radius(x):
    shape_circle_radius = x
    update()

func _set_shape_ellipse_radius(x):
    shape_ellipse_radius = x
    update()
Godot version 3.2.3
in Engine by (125 points)

1 Answer

+1 vote

Are you sure there is no _set() in parent class? That's declared in Object, and I thought everything inherited that.

You can get all property names (including ones you designed, also exported variables) from a node by calling get_property_list(). This returns a big set of dictionaries with a lot of metadata for each property.
(One would extract the names like this:)

func extract_property_names_from_property_list(long_list_of_dictionaries):
    var names := []
    for item in long_list_of_dictionaries:
        names.append(item.name);
    return names

I wrote a custom _set() to help answer this question, observing the manipulated property list to handle the expected behavior of _set() while also extending it:

func _set(property: String, value) -> bool:
    if !(property in property_list): # property_list is a global variable 
        print("property %s is not in property_list" % property)
        return false
    else:
        print("emitting property changed signal now")
        emit_signal("PropertyChanged", property, value);
        return true

FWIW, this is my signal declaration:
signal PropertyChanged(property, value)

This gives me a way to emit signals when I call _set(); but only when _set() is explicitly called, i.e. scale *= 2.0 does not call it or emit the signal. This isn't great because _set() necessitates passing strings around instead of property references, so Godot has to go do lookups based on what strings you pass it.

by (674 points)
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.