Godot: strange bug: Godot sets all the variables of an array to the value that it should only be appending

Created on 16 Jun 2017  路  9Comments  路  Source: godotengine/godot

Operating system or device - Godot version:
Windows 7 - 2.1.2-stable

Issue description:
When appending an item to an array during a while loop, Godot also sets all of the remaining
items in that array to the value that it is appending

Steps to reproduce:
Here is a minimal script to replicate it:

func _ready():
    var l1frames = ["a","b","c"]
    var l2frames = ["a","b" ]
    var l3frames = ["a","b","c"]
    var layers = [l1frames,l2frames,l3frames]
    createVariationMixFrameSequence(layers)

func createVariationMixFrameSequence(layers):  #return array of combinations [[1,1,1],[1,1,2], ..etc]
    var mixEnd = [] #maximum frame number- use that to decide when to move on to the next 
    var newMixResult = []

    for layer in layers:
        mixEnd.append(layer.size()-1)
        newMixResult.append(0)

    var maxItElements = mixEnd.size()-1  
    print("MixEnd:",mixEnd ," MixStart:",newMixResult," elements:",maxItElements)  

    var BANK = [] ## put all resulting mix arrays in here

    var curItElement = maxItElements  

    while curItElement > -1: 
        print("--------iterating on:",curItElement," (",mixEnd[curItElement]+1,")--------")
        while newMixResult[curItElement] < mixEnd[curItElement]:
            newMixResult[curItElement] += 1 
            print(">>>>> New Mix rsult:",newMixResult," <<<<<")

            BANK.append(newMixResult) # <<-- This is where the error happens!
            print("Bank:",BANK) # <<-- This is where the error happens!

        curItElement -= 1 

Here is the output of the last print :

--------iterating on:2 (3)--------
>>>>>New Mix rsult:[0, 0, 1]<<<<<
Bank:[[0, 0, 1]]
>>>>>New Mix rsult:[0, 0, 2]<<<<<
Bank:[[0, 0, 2], [0, 0, 2]]
--------iterating on:1 (2)--------
>>>>>New Mix rsult:[0, 1, 2]<<<<<
Bank:[[0, 1, 2], [0, 1, 2], [0, 1, 2]]

as you can see , the BANK array in the last print - instead of appending the newMixResult, it has also changed all of the other variables in the array to the new mixResult, overwriting them.

Any idea why it's doing that? Am I missing something here? If so, how the hell do I get this to work as intended?

archived

Most helpful comment

You got similar error for all prints.

Also, reading your code, this is expected, as BANK.append(newMixResult) append same reference to same array at each iteration.

All 9 comments

You got similar error for all prints.

Also, reading your code, this is expected, as BANK.append(newMixResult) append same reference to same array at each iteration.

As @Omicron666 says, it's a bug in your script, you're appending the same array all the time; that you modify the array between appends doesn't matter, as you're appending a reference to it.

Ah I see- I am misunderstanding how these while loops work then.

So how do I get the bank array to have a result like this:
Bank:[[0, 0, 1], [0, 0, 2], [0, 1, 2]]...

?
I was expecting that sort of a behavior

@akien-mga , @Omicron666
Are you sure this is just funky behavior in Godot? Did you actually try the code?
Today I resolved it by simply converting what I am appending to the BANK array to a string

BANK.append(str(newMixResult))

which is just such an ugly solution.
Can someone explain to me why it didnt work when I was appending newMixResult in its pure form - an array?

What would the right way to do this be then? Why is simply converting that to a string resolves it?
This is baffling

@blurymind Because, as @Omicron666 said, Arrays are passed by reference. So, when you do newMixResult[curItElement] += 1 you are modifying all references to that array as well, and since all elements of BANK happen to be references to the __same__ array, they all have the same value.

@bojidar-bg Where is bank referenced exactly? The while loop is iterating through the newMixResult[] array
The bank array is defined outside of the while loop and is used only to append items to.
Converting a resulting newMixResult[curItElement] element to a string before appending it to BANK does not modify all the elements in the bank loop, but leaving it in it's array form does alter all the elements in the BANK loop

that is what is confusing me - how come is it that converting it to a sting solves this, while leaving it as an array causes this unwanted behavior

converting it to an integer array prior to appending it also solves it:

BANK.append(IntArray(newMixResult))

giving me a successful final bank of:
BANK =[[0, 0, 1], [0, 0, 2], [0, 1, 2], [1, 1, 2], [2, 1, 2]]

but leaving it as it is -
BANK.append(newMixResult)

Breaks it and causes the unwanted behavior of all of the so far appended elements in bank to be altered to the last newMixResult
Giving me a broken final bank of:
BANK =[[2, 1, 2], [2, 1, 2], [2, 1, 2], [2, 1, 2], [2, 1, 2]]

Yes, because as everyone told you, when you do BANK.append(newMixResult) you append a reference to the newMixResult Array. So if you modify that array (in the next loop), it modifies all references already in BANK. You would get the same result by making your loop without append, and then doing three times BANK.append(newMixResult) at the end.

It works if you use the constructors for String or IntArray as it generates a new object, thus not the mutable reference that you'll continue to work on.

@akien-mga Thank you for explaining it. I think I get it now. Your explanation is the best and it finally got through my thick scull.
Thank you for the patience everyone. Much appreciated.

Was this page helpful?
0 / 5 - 0 ratings