GameMaker (2.3)
Icon

VES Signals

VES Libraries

You must be logged in to obtain assets

Description

This is a generic signaling system. It is inspired by Qt's signals and slots system.

Terminology and Concepts

A slot is a callback

A signal is a string that is fed into the system to trigger slot invocations

A sender is the struct or instance that is sending the signal

A receiver is the struct or instance that is receiving the signal

A connection is the mapping from a sender and its signal to a receiver and its slot

Basic Example

sample_sender = {}
sample_receiver = { calls : 0, slot : function() { calls++; } }

sm_connect(sample_sender, "example_signal_name", sample_receiver, sample_receiver.slot)

sm_emit(sample_sender, "example_signal_name")

Why is this powerful?

The sender and receiver don't need to know anything about each other - they're fully decoupled and are therefore easier to modify and reason about than they would be if the sender had to keep a reference to the receiver and explicitly invoke functions on it. You can also connect a single sender's signal to multiple receivers, enabling the easy usage of one to many callback functionality(e.g. a button that needs to trigger functions on multiple objects).

It's also good for event-like reasoning. For example, if a character in your game dies, it could emit a "character_died" signal and the various parts of the game logic that need to know about a character's death can just connect to that signal. In a sense, that character's death has become an event.

This can also be useful for adding custom logic to child objects. If obj_SpecialDeathCharacter has obj_Character as its parent object, it can connect its own "character_died" signal to its special death logic without any need for modifications of the parent object or any use of GMS-style virtual methods.

Once you get the hang of this system, you'll find yourself using it over and over.

What's Supported?

Both instances and structs are supported for sending or receiving signals. The callback method can be any method or function, it will be executed under a with statement for the receiver. This allows you to combine a function with a struct or use methods that have been declared static.

A single signal can be connected to an arbitrary number of slots.

Signals can carry arguments, which are passed to the slot:

sm_emit(sample_sender, "example_signal", 1, 2, 3)

Once connected, signals can be disconnected:

conn = sm_connect(sample_sender, "sig", sample_receiver, sample_receiver.slot)
conn.disconnect()

You can bind arguments to slots. This can be useful for associating data with particular connections. Bound arguments are always passed before signal arguments:

sample_sender = {}
sample_receiver = { calls : 0, slot : function(b1, b2, b3, s1, s2, s3) { calls++; } }

sm_connect_with_bindings(sample_sender, "example_signal_name", sample_receiver, sample_receiver.slot, 1, 2, 3)

sm_emit(sample_sender, "example_signal_name", 4, 5, 6) // slot will be invoked as if called like: slot(1, 2, 3, 4, 5, 6)
sm_emit(sample_sender, "example_signal_name", 7, 8, 9) // slot will be invoked as if called like: slot(1, 2, 3, 7, 8, 9)

You can daisy chain signals:

sm_connect_signal_chain(sender, "sig1", receiver, "sig2")

You can also daisy chain signals with bindings. This works the same as with slot bindings.

sm_connect_signal_chain_with_bindings(sender, "sig1", receiver, "sig2", 1, 2, 3)

Garbage Collection and Memory Management

For structs, the signal manager will only hold a weak reference to them while not actively working with them so it will not prevent garbage collection.

If the receiver is destroyed, the signal is automatically disconnected.

If the sender has been destroyed and you try to send a signal from it - you're misusing the library.

If you pass a struct as a bound argument, it will not be garbage collected until the signal is disconnected.

Full Public Interface

Global functions:

  • sm_connect(sender, signal, receiver, slot)
    • Connects a sender's signal to a receiver's slot. Returns a Connection.
  • sm_connect_signal_chain(sender, signal, receiver, signal)
    • Connects a sender's signal to a receiver's signal. Returns a Connection.
  • sm_connect_with_bindings(sender, signal, receiver, slot, [bound args...])
    • Connects a sender's signal to a receiver's slot, with arguments bound to the slot. Returns a Connection.
  • sm_connect_signal_chain_with_bindings(sender, signal, receiver, slot, [bound args...])
    • Connects a sender's signal to a receiver's signal, with arguments bound to the signal. Returns a Connection.
  • sm_emit(sender, signal, [args...])
    • Emits a sender's signal, invoking all connected slots and passing in any bound arguments in the connection followed by any arguments passed to sm_emit.

Connection methods:

  • disconnect()
    • Disconnects the connection.

End User Licence Agreement (EULA).

Age Rating: 4+

Version

GMS2.3 - Version 1.0.0. Published May 21, 2021

Loading, please wait

Package contents

Loading, please wait

What is the issue?

Loading, please wait