GMS 2.3+ Making Alarms with Structs

samspade

Member
While I like using the built in alarms when possible, sometimes it isn't. And sometimes it is just fun to mess around with the new things in 2.3, so here's another way to make alarms.

While a little basic, it allows you to create alarms, set some basic values, and add and remove them from sets. Also, because you have access to the internals, they are much easier to modify (e.g. add delta time, pause, etc.) or change in other ways that you might want.

GML:
//create alarm set (not necessary)
my_alarm_set = new alarm_set();

//create an alarm
my_hello = new struct_alarm(room_speed, function() {
    show_debug_message("Hello World");
    my_hello.set();
});

//start an alarm
my_hello.set();

//add alarm to set
my_alarm_set.add_alarm(my_hello);


//update an alarm
my_hello.update();

//update all alarms in a set (this would actually update the my_hello alarm twice so you wouldn't want to both update a set and alarms in the set individually)
my_alarm_set.update();

GML:
// Struct Alarm Script File

function struct_alarm(_default_timer, _func) constructor {
 
    timer = 0;
 
    default_timer = _default_timer;
    my_func = _func;
    triggered = false;
 
    static set_to = function(_value) {
        timer = _value;
        triggered = false;
    }
 
    static set = function() {
        timer = default_timer;
        triggered = false;
    }
 
    static set_default_timer = function(_value) {
        default_timer = _value;
    }
 
    static set_function = function(_func) {
        my_func = _func;
    }
 
    static trigger = function() {
        triggered = true;
        my_func();
    }
 
    static update = function() {
        timer -= 1;
        if (timer <= 0) && (!triggered) trigger();
    }
 
}

function alarm_set() constructor {

    alarm_amount = 0;
    alarm_array = [];

    static add_alarm = function(_alarm) {    
        alarm_array[alarm_amount++] = _alarm;
    }
 
    static remove_alarm = function(_alarm) {
        var _pos = -1;
        for (var i = 0; i < alarm_amount; i += 1) {
            if (alarm_array[i] == _alarm) {
                _pos = i;
                break;
            }
        }
        if (_pos != -1) {
            var _new_array = array_create(alarm_amount - 1);
            for (var i = 0; i < (alarm_amount - 1); i += 1) {
                if (i < _pos) {
                    _new_array[i] = alarm_array[i];
                } else {
                    _new_array[i] = alarm_array[i + 1];
                }
            }
            alarm_array = _new_array;
        }
        alarm_amount--;
    }

    static update = function() {
        for (var i = 0; i < alarm_amount; i += 1) {
            alarm_array[i].update();
        }
    }

}

This was mostly just for fun, though I probably will use them, and I'd be curious about suggestions for additions or improvements.
 
Last edited:

Amon

Member
I wish I could do things like this.

Can you add delta time? i.e. Alarms based on delta time? Please? :)
 

samspade

Member
I wish I could do things like this.

Can you add delta time? i.e. Alarms based on delta time? Please? :)
Just change the update function to be -= dt instead of 1 where dt is a delta time calculation of your choice. Here would be one way to do it:

GML:
...
    static update = function() {
        timer -= argument_count == 1 ? argument[0] : 1;
        if (timer <= 0) && (!triggered) trigger();
    }
...
This would be a simple change to the update function that would allow you to pass in an optional variable (such as delta time).
 

kburkhart84

Firehammer Games
The catch with these things is that you still have to have some object somewhere calling the update function. I get around that in my input system by simply having a controller object instance that gets created and handles things. If I made an alarm system with structs similar to yours, I would likely add an object in the same manner so the user never has to call the update function themselves.
 

samspade

Member
The catch with these things is that you still have to have some object somewhere calling the update function. I get around that in my input system by simply having a controller object instance that gets created and handles things. If I made an alarm system with structs similar to yours, I would likely add an object in the same manner so the user never has to call the update function themselves.
Personally, I don't like that for a variety of reasons (not as lightweight, more hoops to jump through because that alarm object then has to refer to its owner's variables, etc.) and prefer to keep the alarm inside of the object, and in fact not automatically update it unless I want to, and where I want to (e.g. begin step, end step, inside of a state, etc.). But as you pointed out it would be easy to wrap this inside of a instance that controls the alarms so that you don't have to call an update function if you wanted to.
 

kburkhart84

Firehammer Games
Personally, I don't like that for a variety of reasons (not as lightweight, more hoops to jump through because that alarm object then has to refer to its owner's variables, etc.) and prefer to keep the alarm inside of the object, and in fact not automatically update it unless I want to, and where I want to (e.g. begin step, end step, inside of a state, etc.). But as you pointed out it would be easy to wrap this inside of a instance that controls the alarms so that you don't have to call an update function if you wanted to.
To each his own. I try to make systems both as easy to use but with as much control as possible, and sometimes those two features collide. I think the alarm system could still update itself...but you would easily be able to code the object(s) so it does it when you want. You could either have a variable telling you to use beginstep, endstep, etc... or you could turn it off and give them a function to call if they want to do it themselves. My input system never stops updating the inputs, but it DOES allow you to have "manual" inputs where there are function call arguments driving the inputs instead of actual physical devices for input. In the case of an alarm system, I think its important enough to do both automatic updating AND user updating(like you said if they wanted to do it in states).

If you did it right, the system could easily be expanded. Instead of making the user call the update(or not if they are paused), you can simply have a function that does the pause/unpause. Instead of having them maybe call it more/less frequent for changing the speed of time, you could expand your system to have a variable that handles it for them. And then like I said, there can always be the option(even if used temporarily at will) to simply not update but update with a function call. These things may easily be beyond your intentions for this system...just my 2 cents.
 

Amon

Member
Just change the update function to be -= dt instead of 1 where dt is a delta time calculation of your choice. Here would be one way to do it:

GML:
...
    static update = function() {
        timer -= argument_count == 1 ? argument[0] : 1;
        if (timer <= 0) && (!triggered) trigger();
    }
...
This would be a simple change to the update function that would allow you to pass in an optional variable (such as delta time).
I don't know how to calculate the deltatime.
 

samspade

Member
I don't know how to calculate the deltatime.
For that you should get one of the free delta time assets on the marketplace. This one is pretty straightforward. But I think that if you're not familiar with delta time in general, adding it just to an alarm doesn't make sense. Either you use delta time across most of your project, or you don't. You wouldn't just use it for an alarm. I would focus on learning delta time first. If you understand how it works, then using it with an alarm is very easy.
 
Top