Back to the toolbelt

Pulse

The beating heart of your game.

Destress your life by decoupling your hard references with Pulse.

Get it on itch.io >$7.99Current release: v2.0.0

Make things talk to each other without the tangled web of hard references!

What is Pulse.

A flexible signals and events system for GameMaker

Everyone’s had it happen: You delete a chunk of code, or an object, and suddenly a hundred brand new crashes or bugs spring up in your project. This is usually caused by coupling, which is where you reference something else directly, and so removing that thing causes the reference to no longer be valid ergo crash.

A common fix to that issue is called the Publisher/Subscriber pattern, where instead of using direct references, you have things that care about an event (not a GM event, but a ‘happening’) subscribe to a little message channel, and the thing that triggers the event broadcasts a message through that channel. The thing doing the broadcasting doesn’t know who’s subscribed and the things subscribed don’t know who’s broadcasting, they both only care about sending or receiving the message.

Pulse is my take on that pattern, originally started years ago for my own personal use and extended and mutated over time into the feature-packed library that sits in front of you now (feature-packed, sure, but also easy to use…I’m a lazy dev and I want to have to remember and type as little code as possible to get cool things happening).

Light the beacons of Gondor.

As I’ve already said, at its heart, Pulse is two concepts: sending a signal, and subscribing to a signal. That’s it. You don’t need anything else to get enormous value out of it.

So instead of your damage code personally calling the health bar, the audio manager, the VFX system, and whatever else, you do this:

// Somewhere in gameplay code:
PulseSend(SIG_DAMAGE_TAKEN, {
    amount: 10
}, id); 

And then you let everything that cares react on its own:

// In the HUD object:
PulseSubscribe(obj_hud, SIG_DAMAGE_TAKEN,
    function(_data) {
        hp_bar_target -= _data.amount;
    });

// In the audio manager:
PulseSubscribe(obj_audio, SIG_DAMAGE_TAKEN,
    function(_data) {
        audio_play_sound(snd_hit, 0, false);
    });

// In the VFX manager:
PulseSubscribe(obj_vfx, SIG_DAMAGE_TAKEN,
    function(_data) {
        fx_screen_shake(4);
    }); 

Same event. Three listeners with absolutely no direct references between any of them. Add a fourth listener later and you don’t touch the sender at all. Remove one and nothing else notices. This is what it feels like when your code is robust and decoupled instead of being stuck together with hot glue.

Light them again but harder this time.

Simple pub/sub systems are fairly easy to create yourself. What makes Pulse sweet is all the extra features it’s gradually accumulated to fit all the weird scenarios I would hit while making games (and awesome suggestions from people already using it!).

Things like consumable signals (which can get “swallowed” by a particular listener and stop propagating further), being able to filter signals by senders, priorities, one-shot listeners (which automatically unsubscribe after the first signal it receives from that subscription, which is amazing for making tutorials) and even “sticky” listeners (which will receive the last signal sent the moment they subscribe).

Smoke Signals.

Probably my favourite feature of Pulse is being able to use a signal to ask questions. Instead of simply saying “hey this happened”, you can instead ask “this happened, anyone care to respond?”

// Ask every registered vendor what they're offering for this item:
var offers = PulseQueryAll(SIG_GET_OFFERS, { item_id });

// Ask the AI system to pick a target, fall back to player if nothing responds:
var target = PulseQueryFirst(SIG_AI_PICK_TARGET, undefined, noone, player_id); 

Listeners that want to can simply respond with their answer:

PulseSubscribe(obj_vendor, SIG_GET_OFFERS,
    function(_ctx) {
        if (can_afford(_ctx.payload.item_id)) {
            _ctx.Add({ price: my_price, vendor: id });
        }
    }); 

Doing things over time.

Pulse also gives you a bunch of ways to say “send a signal, but not quite yet”, like PulsePost (which queues a signal for later manual processing instead of firing it immediately), and a few variations like PulsePostLatest (which drops previous posts so only the most up to date one is ever processed when the signal processing happens), and PulsePostAfterFrames (which sends after a specific number of manual processing frames).

Plus a lot of machinery around handling queued signals (things like frame signal budgets and queue size limits, so you can’t accidentally code yourself into needing to process a million signals in a single frame, hahaha).

Plus other cool stuff.

There’s a whole host of additional stuff that Pulse supports, mostly coming from weird little corner cases I’ve run into while developing games where the basic pub/sub system doesn’t quite meet the mark.

Things like listener builders (which let you set up stuff about your signal first, before subscribing), buses (so you can keep things like “UI” or “Debug” signals running in completely parallel systems, rather than funneling everything through a single big “signal system”), and a bunch of debugging helpers. Mostly, I feel like if something even vaguely matches the pub/sub pattern, Pulse has been extended to cover it at one point or another.

GET Pulse.

Get Pulse on itch.io

Pulse is also available as part of the Ignition Kit and Full Suite Pass.

Also included: Echo + Echo Chamber.

A purchase of Pulse also includes Echo, my GameMaker debugging toolkit, and it’s “sub-library” Echo Chamber, for free. Echo gives you level-based logging, tags, filtering, optional stack traces, history, file dumps and in-game debugging tools.

Required foundations this library uses

These supporting libraries are required by Pulse.

More from the workshop