• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Free Signals extension [discontinued]

Binsk

Member
Howdy!

Ever since I started using the Godot game engine I fell in love with the idea of signals. I implemented a version for GameMaker that I generally use in my GameMaker projects and thought I would share the code with everyone here.

For those familiar with Godot, these signals work a tad differently because we don't have local functions in GameMaker. However, they are still very useful for dynamically changing / executing code on a per-instance basis and I have the added feature of executing events like you would a script, complete with arguments and returns.

What Is It

Signals are essentially like fancy 'flags' that can be triggered when something happens. You define a signal with a string name for an object and attach either a script or instance/object event(s) to the signal. When the object throws the signal it will execute the linked script / event(s) automatically and pass in any arguments given to the signal call. It is like an execute_script call on steroids.

How Does It Work

The system manages a localized ds_map in the background of any object with a signal. The map contains the signal name as a key and the script / event(s) as the value. When a signal is called it will first check if the signal is defined. If not, it simply returns 'undefined'. If it is then it will execute the script / event(s), pass the arguments provided to the call script, and have the call script return whatever was returned by the called code.

In the case of calling an event, a temporary map data structure is created for the duration of the called event named signal_data. This structure contains the passed in arguments as well as a way to specify a return value.

Example Practical Use?

There are numerous uses for this system, but here are a few:

1) You can treat local events (such as ev_user) like local functions. Since you can now provide arguments and get a return value from event calls you don't have to define global scripts for local instance calculations. This helps clean up code significantly.

2) You can attach instances together by specifying instance events. For example, when I design 'windowing' systems I'll usually have a 'window' object that can contain a variety of different kinds of 'content' objects. I then link things like the 'content update' code of a child content object to a window's signal so that I can just call the signal once when a window changes to update the content. This allows super easy updates that performs correctly no matter what kind of content I have defined or even when no content is attached.

3) You can easily manage dynamic code for sub-types of instances. In a recent RPG system I designed there is an 'ai' instance that represents an AI unit in the battle. All the AI move calculation logic is triggered by calling a signal which easily allows me to use the same object for multiple types of AI that behave differently. I have a default signal defined for the AI behavior but if I want something special (like for a boss) I just attach a different script / event.

Now, the goal of signals is not to provide a way to do something that you couldn't do before. It is a way do something in a different way than before. Some people might really be able to think in terms of signals better than global scripts or child objects while others might find them pointless. It all depends on how you like to program.

Download

You can get the yymp package right here.

I hope you guys find it useful.

Code Example
This example is a bit pointless but it shows how you would make the signal calls:
Code:
// Defining a signal:
var _id = instance_create_layer(100, 100, layer_name, rpg_unit_obj);
with (_id)
   signal_create_event("choose_target", self, ev_other, ev_user0);

// Calling a signal:
var _target = signal_call("choose_target", target_list);
instance_destroy(_target);

// Example signal event:
var _target_list = signal_data[? 0];
signal_data[? "return"] = _target_list[irandom_range(0, ds_list_size(_target_list) - 1)];

// Instance destruction event:
signal_destroy("choose_target");
 
Last edited:

Binsk

Member
Found a bug where signal_call goofed up passing in arguments if you had over 3. Fixed, you can get the fix via the new download.
 
Top