» go back to asset listing

Foreach for GMS2 docs

List of contents:

General idea

This package allows you to mimic foreach directive from other programming languages, to get all values in ds_xxx/array structure one ofter one, using just for loop, without using any ds_xxx_get() functions in GameMaker Studio 2:

for (each(variable, type); as(); iterate()) {
    show_debug_message(string(__key()) + ": " + string(__val()));
}

It takes advantages of how for-loop works against reading ds_xxx/array structures:

for(a; b; c) {
d;
}
  1. first, statement a is executed
  2. statement b is executed and result is checked: if true - d is executed; if false, loop finishes
  3. then statement c is executed
  4. » go back to step 2

What most people forget about, is that b doesn't need to be an expression like i < 10, can just be a value, or return a value. So it can also be a script which returns 0/1.

Thanks to this, we can use scripts on for-loop syntax, as only middle one need to return something, and first/last are just executed without any aftermath:

It works with:

and can also be nested one into another.

In case of stacks, queues and priority lists, a copy of structure will be made, cause only way to get all elements is to remove them at those ds_xxx structures.

A ds_type_array macro is defined in scripts, for better compatibility with other ds_type_xxx variables. Remember it works only with this script int this case, as it's just a 0 value.


Functional documentation


each(variable, type)

First argument needs to be a variable keeping an array, or id of ds_xx structure. The second one need to be a one of following types:

example:

var _array = [1, 2, 3, 4, 5];
for(each(_array, ds_type_array); ....


// or
_dsm = ds_map_create();
_dsm[? "a"] = 1;
_dsm[? "b"] = 2;

for(each(_dsm, ds_type_map); ....

as(), as(key, value)

It get's current value at current position of choosen structure.
If no arguments passed, current position/key can be get using _key() script and current value by _val() functions.
If key and value arguments are passed as strings, then key and value will be additionally asigneed to those values of current instance (doesn't need to exists before).

example:

var _array = [5, 4, 3, 2, 1];
for(each(_array, ds_type_array); as(); iterate()) {
    // _key() - returns 0, 1, 2...
    // _val() - returns 5, 4, 3...
}

// or
var _array = [5, 4, 3, 2, 1];
var _sum = 0;
for(each(_array, ds_type_array); as("position", "something"); iterate()) {
    // _key() / position - returns 0, 1, 2...
    // _val() / something - returns 5, 4, 3...
    _sum += something;
}

iterate()

It just moves to next element or pops from copied stack.


_key()

Returns current key. It will be a string in case of grids, in format X,Y.


_val()

Returns current value, in same type at which it was originally im structure (real, string, array, undefined).


foreach_priority_low_as_first(bool)

Sets in what order priority loops should be returned. By default, on game start low priority (closer to 0) is returned (so same as running this script with argument = 1).
It's remembered, so next foreach will use same setting.

var dsp = ds_priority_create();
ds_priority_add(dsp, "a", 1);
ds_priority_add(dsp, "b", 50);
ds_priority_add(dsp, "c", 100);

foreach_priority_low_as_first(1);
for(each(dsp, ds_type_priority); as(); iterate()) {
    // _key() - returns 1, 50, 100
    // _val() - returns "a", "b", "c"
}

foreach_priority_low_as_first(0);
for(each(dsp, ds_type_priority); as(); iterate()) {
    // _key() - returns 100, 50, 1
    // _val() - returns "b", "c", "a"
}

Nesting

Since you can put arrays into arrays, or ds_xxx into another ds_xxx, this package was made to work with nested structures. It automatically pushes structures into stacks, and garbage-collect values, so you can get back from nested ones to previous one.

var _array = [12, [5,10], [15,20], [30,40]];
for(each(_array, ds_type_array); as(); iterate()) {
    // _key() - returns 0, 1, 2, 3
    // _val() - returns 12, [5, 10], [15, 20], [30, 40]
    if (is_array(_val()) {
        for(each(_val(), ds_type_array); as(); iterate()) {
            // _key() - returns 0, 1 / 0, 1 / 0, 1
            // _val() - returns 5, 10 / 15, 20 / 30, 50
        }
    }
}

foreach_definition()

It defines all global variables needed internally by all other scripts (prefixed by _eachXXXXX). You never need to call this one anywhere, as thanks to gml_pragma() it's automatically called before GameStart event.