1
\$\begingroup\$

I have the following issue. I'm creating a game with different levels. When the level is generated, an array of random number is generated for later use.

If the player leaves the game and come back later, I would like the array to be the same. So I'm trying to save the array when the level is generated. I was thinking about using the function ds_map_add_list(id, key, value).

I'm digging through the documentation, but I'm a bit lost and I have a few questions:

  • How do I create a list from a GML array?
  • If I create a DS Map with the following function inventory = ds_map_create();, what's the scope of the DS Map? Can I use it only in the object holding the code where it was created?
\$\endgroup\$
  • \$\begingroup\$ And why you need to make your array a map? \$\endgroup\$ – DH. Feb 21 '17 at 14:13
  • \$\begingroup\$ Is there a more convenient way to store an array? \$\endgroup\$ – user87553 Feb 21 '17 at 14:25
  • \$\begingroup\$ Depends on what you are storing. May I suggest you to rewrite your question to know which ways you can save in a file your array, and what it is the content? \$\endgroup\$ – DH. Feb 21 '17 at 14:52
1
\$\begingroup\$

Use ds_grid for store 2D array or ds_list for store 1D array. Then you can use ds_..._write function for write data to string (and write that string into file, for example).

Many other ways are possible there, but it depend on what exactly you have in array, etc.

Simple example for 1D array.

/// array_write_as_list(array)

var array = argument0;
var data = ds_list_create();
for (var i=0; i<array_length_1d(array); i++)
{
    ds_list_add(data, array[i]);
}

var result = ds_list_write(data);
ds_list_destroy(data);
return result;

And usage of it:

var s = array_write_as_list(your_array);
// now you can write "s" to file, etc

For reading need to use similar way.

what's the scope of the DS Map? Can I use it only in the object holding the code where it was created?

Any data structures ds_... are global (and you need don't forget to destroy them, when it not need more), but variable, where you stored id of data structure, has own scope. For example, if you use var data = ds_list_create() then you can use data variable only inside current script (and you should destroy this data structure, otherwise there will be memory leak). If you use data = ds_list_create() then you can acces this data structure from any object, using data variable, like ds_list_add(obj_enemy.data, 100);, etc.

\$\endgroup\$
1
\$\begingroup\$

I needed something similar when I was collaborating with a friend on a simple shmup. I was doing development and he was doing design. I exposed various game constants in a plain-text .ini file. He could edit them and modify the feel of the game without having to recompile the code.

I came to the same conclusion you did: I used ds_map. Basically, on startup, my game would run scr_readIni.gml, which set a bunch of variables. Here's an example:

globalvar options;
options = ds_map_create();
ds_map_add( options, "acceleration", "2.5,2.5" );
ds_map_add( options, "maxspeed", "4.0,4.0" );
ds_map_add( options, "subBeatFirePoints", "0.25,0.75" );
ds_map_add( options, "xPixelOffset", "-24,-24" );
ds_map_add( options, "yPixelOffset", "-24,+24" );

I scoped options as a global variable so that the appropriate game objects had access to it. There's probably a more elegant method -- there is in most object-oriented languages -- but this was the approach I settled on.

After these variables were set, I'd check for the existence of my .ini file. If it existed, I'd overwrite the variables I had just set with the ones stored in the .ini file.

if( file_exists( global.configFilename )){

    ini_open(global.configFilename);

    // Read variables : OPTIONS
    // (e.g. movement, allowed firing subbeats, etc.)
    ds_map_replace( options, "acceleration", ini_read_string( "options", "acceleration", options[? "acceleration"]));
    ds_map_replace( options, "maxspeed", ini_read_string( "options", "maxspeed", options[? "maxspeed"]));
    ds_map_replace( options, "subBeatFirePoints", ini_read_string( "options", "subBeatFirePoints", options[? "subBeatFirePoints"]));
    ds_map_replace( options, "xPixelOffset", ini_read_string( "options", "xPixelOffset", options[? "xPixelOffset"]));
    ds_map_replace( options, "yPixelOffset", ini_read_string( "options", "yPixelOffset", options[? "yPixelOffset"]));

    ini_close();

    return true;

// If .ini file doesn't exist, just return (default settings set above)
} else {

    return false;

}

Note that I ended up storing my arrays of reals as a string. When I needed to use them again, I split the string by commas and cast them to reals via a function:

// ds_list_to_reals( input ds_list ) 
//
// Goes through each item and casts it to a real

inDsList = argument[0];

var i;
for( i = 0; i < ds_list_size( inDsList ); i++ ){
    ds_list_replace( inDsList, i, real( ds_list_find_value(inDsList, i) ) );
}

return inDsList;

If I had occasion to revisit this project, I'd probably try to update my code to match the approach taken by Dmi7ry in the other answer.

One last note: if you're interested in saving space, you could simply save the number you're seeding the random number generator with. Then, if you reseed the random number generator with the same number on the next startup, it'll generate the same sequence. Here's a snippet from the official docs:

ini_open("Save.ini")
ini_write_real("Levels", string(level), random_get_seed());
ini_close();

The methods to research are random_get_seed and random_set_seed.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy