• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

SOLVED Tracking global arrays = pointers?

I'm attempting to track global variables over time and store them in a ds_map, where the key is the frame ID and the value is an array of the global variables' values. I'm using the variable_global_get command to do it, but the problem seems to be that the command is grabbing global pointers rather than the values themselves when the data is an array. It works fine when the data is not an array.

Here's my code:
GML:
//Create Event
globalStateMap = ds_map_create(); //ds_map for tracking historical data
globalVariableNames = ["playerHealth","playerHPtest"]; //playerHealth is an array, playerHPtest is not

//End Step Event
var globalValues = [];
var frameID = global.frameCount;
global.playerHPtest = global.playerHealth[1]; //For debug, grab the value from the array
for (var k = 0; k < array_length(globalVariableNames); k++;) //Cycle through each global variable
    globalValues[k] = variable_global_get(globalVariableNames[k]); //Grab the value for each variable, add it into a temp array
ds_map_add(globalStateMap, frameID, globalValues); //Add the array into the ds_map, this is the only time the ds_map is ever modified
Result when P1 loses 4 health on frame 293 (I'm only keeping four frames' worth of data):
#292 New Frame
globalStateMap = { "289": [ [ 0.0, 20.0, 20.0, 0.0, 0.0 ], 20.0 ], "290": [ [ 0.0, 20.0, 20.0, 0.0, 0.0 ], 20.0 ], "291": [ [ 0.0, 20.0, 20.0, 0.0, 0.0 ], 20.0 ], "292": [ [ 0.0, 20.0, 20.0, 0.0, 0.0 ], 20.0 ] }
#293 New Frame
globalStateMap = { "290": [ [ 0.0, 16.0, 20.0, 0.0, 0.0 ], 20.0 ], "291": [ [ 0.0, 16.0, 20.0, 0.0, 0.0 ], 20.0 ], "292": [ [ 0.0, 16.0, 20.0, 0.0, 0.0 ], 20.0 ], "293": [ [ 0.0, 16.0, 20.0, 0.0, 0.0 ], 16.0 ] }
#294 New Frame
globalStateMap = { "291": [ [ 0.0, 16.0, 20.0, 0.0, 0.0 ], 20.0 ], "292": [ [ 0.0, 16.0, 20.0, 0.0, 0.0 ], 20.0 ], "293": [ [ 0.0, 16.0, 20.0, 0.0, 0.0 ], 16.0 ], "294": [ [ 0.0, 16.0, 20.0, 0.0, 0.0 ], 16.0 ] }


Starting on frame 293, when the player went from 20 -> 16 health, the array on every frame is changed to show 16 health. But the non-array variable maintains the correct historical info on each frame.

As best I can surmise, the ds_map entries are being installed as pointers rather than the value for that variable. Is there something special with arrays that's resulting in this pointer-based result? Is there a way to correct this? I have a lot of arrays to track and it's not a realistic option to extract the data into separate variables for each one.
 

FrostyCat

Redemption Seeker
Arrays and structs are passed by reference, not by value like numbers and strings. You have to insert a deep copy of that array into the map if you want it to be completely independent from the original.
GML:
function array_clone_deep(arr) {
    var len = array_length(arr);
    var newarr = array_create(len);
    for (var i = len-1; i >= 0; --i) {
        var val = arr[i];
        if (is_struct(val)) {
            newarr[i] = struct_clone_deep(val);
        } else if (is_array(val)) {
            newarr[i] = array_clone_deep(val);
        } else {
            newarr[i] = val;
        }
    }
    return newarr;
}

function struct_clone_deep(strc) {
    var keys = variable_struct_get_names(strc);
    var len = array_length(keys);
    var newstrc = {};
    for (var i = 0; i < len; ++i) {
        var key = keys[i];
        var val = variable_struct_get(strc, key);
        if (is_struct(val)) {
            variable_struct_set(newstrc, key, struct_clone_deep(val));
        } else if (is_array(val)) {
            variable_struct_set(newstrc, key, array_clone_deep(val));
        } else {
            variable_struct_set(newstrc, key, val);
        }
    }
    return newstrc;
}
GML:
ds_map_add(globalStateMap, frameID, array_clone_deep(globalValues));
 
Well that explains that! Thanks so much for this and for the code. Looking forward to implementing this and getting past this issue :) Also nice to improve my understanding of GML, I had no idea that's how it worked.
 
Top