GML What's the Difference: Loop Structures vs. Step Checks and Alarms

Discussion in 'Tutorials' started by FrostyCat, Dec 26, 2017.

  1. FrostyCat

    FrostyCat Member

    Jun 26, 2016
    What's the Difference: Loop Structures vs. Step Checks and Alarms

    GM Version: GMS 2.x, GMS 1.x
    Target Platform: All
    Download: N/A
    Links: N/A
    Summary: An explanation of when to use loops and when not to, plus a brief overview of non-blocking repetition

    Many recent novices needlessly crash their projects by misusing while, do-until and other blocking loops. This guide aims to document situations where a loop should or should not be used, plus the basics of asynchronous repetition for when loops are inappropriate.

    Blocking (synchronous) vs. Non-Blocking (asynchronous)

    An operation is blocking or synchronous if it halts the runtime environment until it is done. Examples include the basic looping statements (for, while, do-until, repeat, with) and the blocking message functions (e.g. get_string(), show_message()).

    An operation that runs in the background without halting the runtime environment is non-blocking or asynchronous. Examples include the step/draw event cycle, HTTP requests and non-blocking message functions (e.g. get_string_async(), show_message_async())

    When to Use Loops

    Loop structures such as for, while, do-until and repeat are blocking operations and must always be used with care, particularly while and do-until. Loop structures should only be used:

    - When it is OK to repeat the action(s) all at once without waiting for the next step
    - When the state of execution is invalid or unwanted until the looping condition is satisfied
    - When action(s) taken within the loop work toward satisfying the looping condition (this includes the increment in a for loop)

    Examples of when to use loops:

    - Finding a random empty spot
    var xx, yy;
    do {
      xx = random(room_width);
      yy = random(room_height);
    } until (position_empty(xx, yy));
    - Reading all lines from a file
    var str = "",
        f = file_text_open_read("file.txt");
    while (!file_text_eof(f)) {
      str += file_text_read_string(f) + chr(10);
    - Generating a cluster of effects
    for (var dir = 0; dir < 360; dir += 45) {
      effect_create_above(ef_firework, x+lengthdir_x(128, dir), y+lengthdir_y(128, dir), 1, choose(c_red, c_yellow, c_lime));
    - Drawing copies of an image, shape or piece of text
    var xx = x;
    repeat (lives) {
      draw_sprite(spr_heart, 0, xx, y);
      xx += sprite_get_width(spr_heart);

    When NOT to Use Loops

    NEVER check a condition in loop form if you answer "yes" to any of the following:

    - Do the actions within need to repeat gradually over time in order to be meaningful?
    - Do you need other background processes to continue running while it is repeating? (e.g. speed-based movement, alarms/countdowns, user input, networking packets)
    - Do the actions that work toward the stopping condition lie outside the block?
    - Does the repeating condition rely on a background process to work properly?
    - Does the repeating condition involve real-time user input? (e.g. keyboard, mouse, gamepad and touch screen presses)

    Examples of when NOT to use loops:

    - Fading effects
    while (image_alpha > 0) {
      image_alpha -= 0.1;
    Moving towards a target
    x = 0;
    repeat (room_width div 4) {
      x += 4;
    Waiting for user keystrokes
    while (keyboard_check(vk_up)) {
      y -= 2;
    Checking a winning condition
    do {
      effect_create_above(ef_rain, 0, 0, 1, c_white);
    } until (!instance_exists(obj_enemy))
    Exception: It is OK to use a loop to help compute a condition, as long as it is finite and self-contained. For example, it is OK to use a loop to help search the rows on a tic-tac-toe board for a win as part of a step check (more on that later), but NOT OK to use a loop to keep music playing while nobody has won.

    Asynchronous Repetition

    Now that you know what kinds of repetition should and shouldn't be handled with loops, you should learn what to do with those that shouldn't. The Step and Alarm events are your primary tools for handling these.

    Step Checks

    The step check is the most basic form of asynchronous repetition. It takes the form of an if statement in the Step event (sometimes Begin Step or End Step), which repeats once upon every frame while the game runs. Conditions appropriate for step checks include most user input, winning conditions and collision checks, among others.

    Example: "While the right arrow key is held down, move 5 pixels to the right"

    if (keyboard_check(vk_right)) {
      x += 5;
    Controlling Step Checks

    Sometimes you need to control the repetition of step checks, for example playing a sound when the player is hit but not while it stays hit. This requires you to implement a flag variable, which is covered in this article: Restricting Unwanted Repetition with Flags

    Collision, Keyboard and Mouse Events

    GM comes with a rich variety of built-in events that operate like step checks. Consult your version's Manual for a full listing and what condition each event corresponds to. You can use these as needed to visually delineate code and inherit event behaviours separately.

    Caution: In a collision event, other represents the colliding instance. The same is NOT TRUE in a step check. If you need to access the colliding instance from a step check, you must use a collision-checking function that returns an instance ID, then access the colliding instance through that.

    Collision event with foo:
    with (other) {
    Equivalent step check:
    var inst = instance_place(x, y, foo);
    if (inst != noone) {
      with (inst) {
    Delayed Repetition Using Alarms

    To space apart recurrences beyond once per step, alarms offer a simple solution. Each object comes with up to 12 alarms, numbered 0 through 11. Once an alarm's event is filled in and the alarm is set to a value above 0, it will decrement by 1 every step until it reaches 0, at which point the alarm's event is executed once.

    To repeat one or more actions every q steps using alarm n:

    Alarm n event:
    /* Action(s) */
    alarm[n] = q;
    To start the cycle: (you can also set to 1 to cut to the first recurrence faster)
    alarm[n] = q;
    To stop the cycle: (note that this will not run the upcoming recurrence)
    alarm[n] = -1;
    Caution: Be careful about setting alarms in the Step event or any other recurrent event. If you want to avoid setting the alarm again while it is counting down, you should check that the alarm is unset (i.e. alarm[n] < 0) before setting the alarm.

    Variation: Delayed Repetition Using Step Checks

    See "rate-capping flags" in: Restricting Unwanted Repetition with Flags

    The cycle can be started by setting the flag to true, and stopped by setting the flag to false.
  2. Morne

    Morne Member

    Jun 20, 2016
    Excellent advice! Especially:
    Should be a sticky
  3. appleWolf

    appleWolf Member

    Jun 27, 2016
    If I'm bumping this, it deserves a bump. Thank you for taking the time to do this; haven't been using GM in years.
    SilentxxBunny likes this.
  4. Tofu Heavy Industries

    Tofu Heavy Industries Member

    Feb 2, 2018
    Simple, important, a needed read. no yt video needed. I hope its ok to :bump:
    SilentxxBunny likes this.
  5. Joe Banko

    Joe Banko Member

    Jun 21, 2019
    Amen to the needs to be a sticky!
    SilentxxBunny likes this.
  6. SilentxxBunny

    SilentxxBunny Epsilon

    Jun 21, 2016
    Thank you for this information (and also for taking the time to format it so nicely.) I find programming tutorials hard to read, and even harder to digest. On the contrary: I found this one to be very clear and concise. I've bookmarked this topic for quick reference.
    ... I love you

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice