YoYoGames / GameMaker-Bugs

Public tracking for GameMaker bugs
26 stars 8 forks source link

Array_to_list(), list_to_array(), struct_to_map(), map_to_struct() #3227

Open tinkerer-red opened 1 year ago

tinkerer-red commented 1 year ago

Is your feature request related to a problem?

Often there is minimal to no concern with doing this manually, despite it being fairly slow. Although with the advent of arrays replacing lists, and the older collision_list functions this makes it a bit more difficult to explain to newer devs. Adding this would also be beneficial to save time from the need to convert to a json then to a struct or map which can be quite costly for larger datasets.

Describe the solution you'd like

var _list = ds_list_create();
var _count = collision_line_list(0,0,100,100,obj_enemy,true,true,_list, true);
if (_count) {
    var _arr = list_to_arry(_list)
    // or
    var _arr = []
    _arr = list_to_arry(_list, _arr);
}
ds_list_destroy(_list)

Describe alternatives you've considered

Doing it by hand with a for/repeat loop works well enough for lists, although the recursion for structs and maps is more annoying.

Another option would be to have collision_*_array().

unfortunately for arrays, the built in collision checks are far faster then anything we would be able to make. So they are to valuable not to use, by trying to make your own collision array functions.

Additional context

No response

Tazdraperm commented 1 year ago

Honestly, I hope ds_* will be removed in the future and replaced with better garbage-collected alternatives

DiasFranciscoA commented 1 year ago

@Tazdraperm There is an issue with completely removing the usage of ds_*, particularly ds_map. The problem lies in the fact that ds_maps serve a specific purpose, even though many users may not be aware of it.

When you create a ds_map or a struct, a runner dictionary is utilized to maintain the mapping between hash and keys. This is necessary because eventually, you may want to use json_stringify or json_encode, and the runner needs to understand the conversion from hashes to actual keys.

Now, for ds_maps, these mappings are stored locally for each map. This means that if you create a ds_map and add 1 million entries to it, where keys range from 0 to 1000000, this cache is destroyed as soon as you call ds_map_destroy. The major distinction for structs is that hash mapping is global for all structs. This allows us to make the hash independent of the struct itself. For example, the hash for "maxHealth" is the same for structA and structB. However, it also means that all that data is stored throughout the entire game execution.

This distinction becomes evident when you call variable_get_hash(i) within a loop where i ranges from 0-1000000. Simply accessing the hash will cause GameMaker to store it in that global internal dictionary. Consequently, this significantly increases memory usage and decreases the FPS rate.

Therefore, your implementation and intended usage will ultimately determine the best approach. If you require a "lookup table" where the keys hold no specific meaning, it is advisable to stick with ds_maps and avoid structs. Given these considerations, it is likely that ds_* functionalities will continue to be supported in the runner.

NOTE: they might eventually get their own ref type or even become garbage collected at some point in time, though.

tinkerer-red commented 12 months ago

Made these yesterday thought i would share until an official option has been released. Currently this implementation is faster then array_foreach()

function list_to_array(_list, _arr=undefined) {
    gml_pragma("forceinline");
    var _size = ds_list_size(_list);
    if (_arr == undefined) {
        _arr = array_create(_size, undefined);
    }
    else {
        array_resize(_arr, _size);
    }
    var _i=0; repeat(_size) {
        _arr[_i] = _list[| _i];
    _i+=1;}//end repeat loop
    return _arr;
}
function array_to_list(_arr, _list=ds_list_create()) {
    gml_pragma("forceinline");
    var _size = array_length(_list);
    var _i=0; repeat(_size) {
        _list[| _i] = _arr[_i];
    _i+=1;}//end repeat loop
    return _list;
}