Skip to content
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

Add synchronized variables / pointers without functions #2817

Open
MaaaxiKing opened this issue Jun 3, 2021 · 5 comments
Open

Add synchronized variables / pointers without functions #2817

MaaaxiKing opened this issue Jun 3, 2021 · 5 comments

Comments

@MaaaxiKing
Copy link

MaaaxiKing commented Jun 3, 2021

Describe the project you are working on

Logic game in which many variables of a Node's children (and also their children!) are changed oftenly and need to be updated in the parent Node.

Describe the problem or limitation you are having in your project

I always must do $"..".some_var = some_var or call the setter function after assigning a new value and wanting it to be known in the parent.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

You could just assign a variable a new value and it automatically updates in other Nodes where it is used as a pointer.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

var *some_var = $SomeChild.some_var
Btw, something like this is possible in C# (adding * in front of a function's parameter)

If this enhancement will not be used often, can it be worked around with a few lines of script?

No, not directly this.

Is there a reason why this should be core and not an add-on in the asset library?

Yes, it could be used in any project.

@dalexeev
Copy link
Member

dalexeev commented Jun 3, 2021

Objects, arrays and dictionaries are already passed by reference, not by value. Adding pointers to a scripting language is a controversial proposal. And this is a wrong situation when one value is stored in several places and should always be automatically synchronized. The value should be stored in one place, we can only simplify access to it from different places.

@Xrayez
Copy link
Contributor

Xrayez commented Jun 3, 2021

I think your particular problem could be solved with signals.

You can also go though Resources documentation.

You can create custom resources via class_name CustomResource extends Resource and you can load them via script. If you load the same resource with preload() or load(), it will be shared across all instances of your parent/child nodes. You can also emit changed signal on resources to respond to data changes, effectively synchronizing node states.

See also #1734 for a more specific use case.

@Xrayez
Copy link
Contributor

Xrayez commented Jun 3, 2021

Found a previous proposal which I think shares some similarities: godotengine/godot#6491.

@willnationsdev
Copy link
Contributor

willnationsdev commented Jun 4, 2021

I agree with them, a Dictionary, or if you want statically typed variables, a custom Resource, would be a better solution that doesn't add any further complexity to the language. You need only pass a reference to the Dictionary/Resource from the parent to the child. Possibilities include...

  1. passing from parent to child during instantiation if the parent creates and adds the child manually.
  2. pulling from a getter on the parent whenever the child's NOTIFICATION_PARENTED notification triggers.
  3. pulling from an actual specific resource in the project that is intentionally maintained as a separate data source on the filesystem (i.e. each script just loads the same resource at the same file path and therefore ends up with the same object reference).
  4. using groups. That is, mark nodes at the top of each sub-tree as a "data_owner" by putting them in that group. Then, on _enter_tree, have each node in the tree search up through its ancestors until it reaches a node in the "data_owner" group. When it finds one, it checks to see if it has a getter for the data and if so, calls it to acquire the data. This way, the nodes don't necessarily need to have a constant chain throughout the subtree. You could break it up and they would still successfully grab the data from their eventual ancestor.

Here's an example of option 2.

# ancestral_data.gd
class_name AncestralData
extends Resource

var some_var := ""

# composite_node.gd
extends Node

export var data: Resource = null

func get_ancestral_data() -> AncestralData:
    return data as AncestralData

# This node reinforces the Composite Pattern.
# It, and other types that share an interface with it, will
# will interchangeably perform similar logic seamlesssly.
# In this case, they will uniformly pass along AncestralData
# if a `get_ancestral_data` method is present in the parent
# and they have this notification implemented identically.

func _notification(what):
    match what:
        # Executes when this node is attached to a parent node
        NOTIFICATION_PARENTED:
            var p = get_parent()
            if p.has_method("get_ancestral_data"):
                var parent_data = p.get_ancestral_data()
                if parent_data:
                    data = parent_data

Or, in a simpler, less "safe" or "precise" way, just quickly grabbing the parent property, as you were probably doing before.

# composite_node.gd
extends Node
export var data: Resource = null
func _enter_tree():
    data = get_parent().data

@dugramen
Copy link

dugramen commented Aug 9, 2021

You could just use setters and getters right?
Instead of this:

var *some_var = $SomeChild.some_var

You can already do this:

var some_var:
    set(val):
        $SomeChild.some_var = val
    get:
        return $SomeChild.some_var

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants