GameMaker Language currently doesn't have closures.
It might in GMRT (proposal), but for now you cannot do something like this
function get_enemies_below_hp(_enemies, _threshold) { return array_filter(_enemies, function(_enemy) { return _enemy.hp < _threshold; }); }
because _threshold
is not visible inside the inner function.
But there are some ways around that.
First, the code. See if you can spot what's going on here right away:
function get_enemies_below_hp(_enemies, _threshold) { with { _threshold } return array_filter(_enemies, function(_enemy) { return _enemy.hp < _threshold; }); }
And here's what happening:
-
{name}
is a shorthand for{name:name}
- Function literals bind to the current
self
, which inside thewith
-block would be that struct we just made. - Non-local/non-global variables inside the functions read from
self
, which is that bound struct.
So on the lower level the code works somewhat like if you did this:
function get_enemies_below_hp(_enemies, _threshold) { var _struct = { _threshold: _threshold }; var _function = method(_struct, get_enemies_below_hp_anon_1); return array_filter(_enemies, _function); } function get_enemies_below_hp_anon_1(_enemy) { return _enemy.hp < self._threshold; }
And if you need to retrieve some values for manipulation afterwards, you can do that too:
function count_enemies_below_hp(_enemies, _threshold) { var _context = { _threshold, _found: 0 } with (_context) array_foreach(_enemies, function(_enemy) { if (_enemy.hp < _threshold) _found += 1; }); return _context._found; }
Bonus: using self
Want to pass in some variables but still have access to the instance that called the function? Pack it up and pass it in:
function count_enemies_below_hp(_units, _threshold) { var _self = self; var _context = { _threshold, _self, _found: 0 } with (_context) array_foreach(_units, function(_unit) { if (_unit.team != _self.team && _unit.hp < _threshold ) _found += 1; // or: with (_self) { // closure-struct is now in `other` if (_unit.team != team && _unit.hp < other._threshold ) other._found += 1; } }); return _context._found; }
(note: can't do _self: self
because that would refer to the struct being made)
And that's about it!
Truly good C:. With statement is the top GameMaker feature
little correction: on the bonus with(_self) statement you forgot to use other._found+=1 instead of _found+=1.
Fixed, thank you
Interesting! I worked out how one could do closures with the struct/method strategy but I never imagined “with” could be used in this way.
Speaking of using `with`. I found out that `with` skips on `undefined` which can be used in the following shorthand.