-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
When using a setter for a field, if the backing field is only updated after a signal, then values are not correct #34698
Comments
I believe it's not a bug.
func _ready() -> void:
var error := Child.connect('child_value_set', self, '_on_child_value_set')
assert(error == OK)
self.parent_value = parent_value
There is a third problem: func _set_parent_value(new_parent_value: bool) -> void:
# Uncommenting this makes it work, yes.
# but it should be fine as long as the values were in sync
# when the scene was last saved.
# The scene loader will have the same value to set in the child node,
# because you decided to export it too.
parent_value = new_parent_value
if Child != null:
Child.child_value = new_parent_value About the present use case, here is a personal suggestion: if the goal is to expose the child API in the parent node as a shortcut, you don't need a signal to make this work. This should be enough: # Parent.gd
tool
extends Node2D
export(bool) parent_value setget set_value, get_value
onready var Child = $Child
func get_value():
if Child != null:
parent_value = Child.child_value
return parent_value
func set_value(v):
if Child != null:
Child.child_value = v
parent_value = Child.child_value However the fact this would cause a duplication of the value in the scene file (and duplication of setter calls) may not be ideal, and it can be fixed using property hints. There is no reason to save the parent value (because the child already is), and no reason for it to even have a backing field. Unfortunately there is no "easy" way to specify hints in GDScript (see #20318), but there is a way to create such binding: tool
extends Node2D
onready var Child = $Child
func _get_property_list():
return [
{
# This creates a property which shows up in the inspector,
# but is not actually saved in the scene and has no backing field.
"name": "parent_value",
"type": TYPE_BOOL,
# `export` is equivalent to PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE.
# Since all we want is to expose a shortcut, we can omit storage.
"usage": PROPERTY_USAGE_EDITOR
}
]
func _get(property_name):
if property_name == "parent_value":
# No need to check for existence anymore,
# because it will only be accessed in inspector
# once the node will be in the scene already.
# The child node BECOMES the backing field,
# it is then always in sync.
return Child.parent_value
func _set(property_name, value):
if property_name == "parent_value":
Child.parent_value = value |
Using Without With Are you sure that connections are persisted? I'd expect that they're persisted if they're declared in the editor, but my tests indicate that connections set through code are not persisted in the scene.
Whether the script is This repro project is a greatly simplified version of the original version. In the original, I'm instancing scenes and changing their properties before they enter the
Doing this means that we have to wait until This also complicates the example given where we use a getter. I could use a getter, but IMO it's actually bad practice in this instance as calling the getter before adding to the I wasn't aware of |
If your goal is to keep the two values in sync, then there is a small problem from the beginning: your scene begins with the values NOT in sync.
I added prints to double-check, and here is what happens when the scene is instanced:
So in this design, it sounds like the parent node is the one "owning" the true value the node should have (important concept, single source of truth). So the backing field of the parent must be set, otherwise you are loosing the information that it is initially set to
My mistake, I'm confusing this with a slightly different situation. Signals are not persisted unless they are flagged as such. Ignore what I said about it. |
Ah you're right, that was the root issue. It's obvious looking back on it now but if Here is the correct code:
Thank you for your help and sorry for the false report! |
Godot version: 3.1.2.stable.official
Issue description: This one's a bit convoluted, so I think it's easier to describe via an example.
Say that we have two scripts,
Parent
andChild
. Both of them have an exported field,Parent.parent_value := false
andChild.child_value := false
, and we want to keep them in sync. (Parent
essentially exposes the public API for this scene.)Parent.parent_value
has a setter. This setter does not set the backing field directly and instead setsChild.child_value
with the new value.Child
then sends a signal back up toParent
that itschild_value
has changed.Parent
then from the signal callback sets its backing field fromChild.child_value
.When
Parent.parent_value
is set totrue
in the scene and then launched, this value saved in the scene is not propagated toChild.child_value
as expected. (The scene-saved value is passed through in_ready
.)Additionally, if both
Parent
andChild
are set astool
scripts, then when settingParent.parent_value
totrue
in the scene and the scene is closed and reopened, the value is not saved in the scene at all. (It's unclear if these two scenarios are the same bug or even related, so I didn't bother opening two issues.)Steps to reproduce: This one's probably easier to look at the repro project.
Minimal reproduction project:
test.zip
The text was updated successfully, but these errors were encountered: