godot: Exported (non-static) Arrays are not duplicated when instancing.
Godot version: Godot 3.0
OS/device including version: Solus Linux / X11
Issue description: When exporting an array variable in GDScript, the array is not duplicated upon instancing of the node. This behavior conflicts with all other properties, and is contrary to the OOP model Godot uses. The array is not static (or class-wide), but belongs to the instance, and so every instance’s copy should be unique. Currently, unless manually duplicated, modifications to the array affect all instances of the class.
This issue may be present with Dictionaries as well, I have not tested them.
Steps to reproduce: Write this script:
extends Node
export var test = []
# This way too, it's not bound to explicit array constructor
# export(Array) var test
func _ready():
test.append("test %s" % self)
print(test)
Save a node with the script a as scene, instance two copies at runtime, and add them to the tree.
Expected result:
["test Node:XXXX"]
["test Node:YYYY"]
Result:
["test Node:XXXX"]
["test Node:XXXX", "test Node:YYYY"]
Minimal reproduction project: See above.
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 11
- Comments: 21 (17 by maintainers)
I can’t see what exactly is your point because it’s just an expected behavior in OOP.
You can try to reproduce it using Python.
You can “avoid” this behavior initializing the array in the init/ready method.
In the
export var
case, you can just leave the variable empty (null
), so in the following code the array will be unique:However, I think it’s an inconsistency in gdscript implementation because both
var
andconst
should share objects created before initialization. In this case onlyconst
is being shared.Additionally, and ancillary to this, when instancing a node in the editor with an exported array on it, the array is not duplicated, leading changes to the array on one (child) node to be applied across all instances and even the original scene. This is an unacceptable behavior, for very obvious reasons.
If this was fixed in 4.0, it seems there was a regression at some point:
<start>
was added in the inspector in the NonEmptyArray scene to trigger the issue. I expected NonEmptyArray to print["<start>", "NonEmptyArrayX"]
instead of all the nodes, like in the EmptyArray case (sans<start>
).Full project: ArrayRegression.zip
If intentional, https://github.com/godotengine/godot-docs/issues/5640 should be extended to 4.X docs as well.
Edit: the issue also happens in 4.2 dev 5
You are correct that this is an expected behavior in Python, but that’s because variables declared with that syntax reside in the class, not the instance, and are initialized at declaration time.
What you describe is the realm of static member variables in almost all OO languages (C++, C#, D, etc.), which brings me to my next point; In GDScript, static member variables are explicitly disallowed:
In GDScript, the correct behavior is that all variables are instance-local and should be initialized upon initialization of the script (i.e. just before the
_init()
method).If it does what I think it does, it should fix half the problem.
The ideal solution (AFAIK) is to make instances have a copy-on-write property in the editor, and otherwise duplicate the value at runtime. This will allow changes to the parent scene’s copy to properly propagate, while allowing every instance their own copy.
Yeah, there may be a good reason for it, but I agree it’s definitely surprising. I wonder if it would be possible to find more use for the
static
keyword. GDScript is very much a class language. Would make sense to have static variables.This!
I can’t see why specifically array should work differently from everything else. All other exported variables and node specific properties (Size, Position and so on) are instance-local!
And again, this behaviour has changed since 2.x, what is the logic behind this change?
Can confirm in 4.1.3. even while being documented earlier this behavior is a huge gotcha that feels out of place compared to how a user would expect a variable to behave.
Also I’d suggest updating the docs with a warning about this until it’s fixed because as of now there is no mention of it which only adds to the confusion
This design decision is just confusing. Why is every variable local except of arrays? Does not make any sense.
This is the expected behavior according to the doc. Though I don’t know the reason for that.