GameMaker: applying “with” to multiple objects

As you may know, GameMaker Language has a Pascal-like "with" structure:

with (value) { code }

but it's not exactly like the Pascal version. In GameMaker, it can take an instance:

var bullet = instance_create(x, y, obj_bullet);
with (bullet) direction = 180;

or an object type (and will apply the expression to each instance of it):

with (obj_bullet) instance_destroy();

This can be rather handy under the multiple circumstances.

However, initially the same block can not be applied to multiple values, and that's less handy.

Sometimes you can cheat by applying the expression to a shared "parent" type of objects, but that is not always the case (and can have side effects if there are more object types meeting the condition).

So let's consider your options for applying a piece of code to all instances of several types:

1. The easy way

The simplest solution is to include multiple blocks of code (one for each value):

with (obj_a) if (y < 0) instance_destroy();
with (obj_b) if (y < 0) instance_destroy();
with (obj_c) if (y < 0) instance_destroy();

It is not ideal and duplicate code means more trouble with editing, but this is most often used.

2. Scripts

When presented with a snippet of code that is used in multiple places it's a pretty reasonable thing to move it into a script. This applies here as well. So, if you were to make a script being

/// scr_check_outside()
if (y < 0) instance_destroy();

you could then simply call it from each with-loop:

with (obj_a) scr_check_outside()
with (obj_b) scr_check_outside()
with (obj_c) scr_check_outside()

Also, contrary to the popular belief, scripts aren't that slow - in fact, a script call alone takes about the same time as a variable assignment does, so the overhead is neglectable for most cases.

3. Arrays

Here's to another reasonable thing - pushing the things in question into an array:

type[0] = obj_a;
type[1] = obj_b;
type[2] = obj_c;
types = 3;
for (var t = 0; t < types; t++) {
    with (type[t]) if (y < 0) instance_destroy();
}

In most situations you can also make the array global or instance-specific to avoid extra reallocations.

4. Switch-trick

And now for something different.

In languages with array declaration syntax, you could do something like this for the array approach:

/// note: not a valid GML snippet
var type = [obj_a, obj_b, obj_c][k];

Now, of course, this is not something that all compilers would optimize, but if you think about it, a lookup on a "constant" array of values can as well be replaced with a switch-statement:

var type;
switch (k) {
case 0: type = obj_a; break;
case 1: type = obj_b; break;
case 2: type = obj_c; break;
default: // error
}

While GML does not have array declarations, the same principle can be applied:

for (var k = 0; k < 3; k++) {
    var type;
    switch (k) {
    case 0: type = obj_a; break;
    case 1: type = obj_b; break;
    case 2: type = obj_c; break;
    }
    with (type) if (y < 0) instance_destroy();
}

Which, while not the most compact, has potential to be the fastest approach (especially on YYC).

5. select()

As I have previously mentioned, GameMaker does not allow for array declarations in their common form. However, there's another trick: GameMaker has a JavaScript-like "argument" array for managing variable argument counts in scripts. This allows to do this little thing:

/// select(index, ...values)
return argument[argument[0] + 1];

What this does is that it returns an argument of index defined by the first script argument. So select(0, "a", "b", "c") will return "a", and replacing 0 with 1 or 2 will return "b" or "c" accordingly.

And this can be put to a good use for this matter:

for (var k = 0; k < 3; k++) {
    with (select(k, obj_a, obj_b, obj_c)) {
        if (y < 0) instance_destroy();
    }
}

Which quickly becomes the shortest way to go about this as the case count grows.


In conclusion, the problem can be solved in a variety of ways, allowing to pick based both on situation and personal preferences.

Related posts:

Leave a Reply

Your email address will not be published. Required fields are marked *