Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
3

How do I create a system of events?

Asked by 8 years ago

Okay, what I'm looking for is maybe an explanation or a link to an article that can fill me in on the techniques of event-driven programming in lua. I know that Roblox already has a functioning event based system with objects like BinableEvent and the Event properties which fire upon certain changes to a part.

If you don't know, Event-Driven programming is an important way to fashion your code so that changes to code will happen when other changes to the code occur. Let's say I click a button, making that button into a clicked state. A code designed to run after the button is clicked will now act because I clicked the button, which fired an event that signified it's clicked state.

The challenge here, is that I want to know a feasible way of implementing these kinds of events without using the Roblox Tools - but only conventions of the lua programming language itself. This would enable me to take my game outside of roblox if I so desired.

From a basic lookup on stackoverflow, I could find that the game/program I would be programming would have to be split up into multiple frames. Any changes made to the internal data would be loaded with the start of the frame, and as the frame passes, variables are checked against prior data. If a change is detected, a callback function will fire.

For example, this will print "a changed from 5 to 7" when a variable is changed in the SomeFunctionThatOperatesTimefunction

WatchedVariables = {
    a = 5,
    b = 22,
}
WatchedVariables_cache = {}
for k,v in pairs(WatchedVariables) do
    WatchedVariables_cache[k] = v
end

function OnFrame()
    print("NEXT FRAME! (possibly 1 second later or something)")
    for k,v in pairs(WatchedVariables) do
        local v_old = WatchedVariables_cache[k]
        if v ~= v_old then
            -- this is the "callback"
            print(tostring(k).." changed from "..tostring(v_old).." to "..tostring(v))

            WatchedVariables_cache[k] = v
        end
     end
 end

 function SomeFunctionThatOperatesSomeTime()
     print("about to change a, brother!")
     WatchedVariables.a = -7
     print("a is changed")
 end

Although I get the mechanics of how it works, the only callback functions I've found as examples are repetitive calls to the print function. Is there a way to utilize some sort of callback function that provides a signal or flag as versatile as how roblox implement's it's "Fired" methods in BindableEvents? Maybe I'm just not seeing the big picture yet I'd love any advice. Sorry, this concept might be big.

1 answer

Log in to vote
2
Answered by
EgoMoose 802 Moderation Voter
8 years ago

This is a good question!

You actually don't have to use bindable events however it's recommended to keep threading identical to that of Roblox's signals, although to keep code shorter I won't use them in my example, same idea though.

By using meta tables we can essentially create a middle man between the table that has the data we want and the coder. We can then use the metatables of this middle man to check if a value being added is new, and if it is, fire an event. It's really that simple:

local this = {};
local events = {};
local proxy = setmetatable({}, {
    __index = this;
    __newindex = function(t, k, v)
        if events[k] and this[k] ~= v then
            -- fire event
            events[k](v);
        end;
        -- new value being passed, update
        this[k] = v;
    end;
});

proxy.cats = 10;
proxy.likesCats = true;

events.cats = function(value)
    print("cats was changed to", value);
end;

events.likesCats = function(value)
    print("likesCats changed to", value);
end;

proxy.cats = 5;
proxy.likesCats = false;

print(proxy.cats);

Hope that helps!

Edit:

If you wanted to add a wait then you could do it like this. Although honestly speaking I think a bindable event is better for this.

local this = {};
local events = {};
local proxy = setmetatable({}, {
    __index = this;
    __newindex = function(t, k, v)
        if events[k] and this[k] ~= v then
            -- fire event
            events[k](v);
        end;
        -- new value being passed, update
        this[k] = v;
    end;
});

function proxy:wait(index)
    local current = self[index];
    repeat wait() until current ~= self[index];
end;

proxy.cats = 10;

delay(5, function()
    proxy.cats = 5;
end);

local t = tick();
proxy:wait("cats");

print(tick() - t, "cats: " .. proxy.cats);
0
I think I get it. You can change the callback code of a specific even easily with this example. One more thing: how could you implement the wait() method on an event, which suspends code until that event is fired? randomsmileyface 375 — 8y
0
Added another example for you. EgoMoose 802 — 8y
0
Thank you! randomsmileyface 375 — 8y
0
I'm assuming that for things that only pass by reference (like tables), to keep track of their changes would require recusion searching through the table, right? randomsmileyface 375 — 8y
View all comments (2 more)
0
Yes, but if you're smart with the meta-tables and set them up right you can use a parent child relationship to fire events by tracing back the family line. EgoMoose 802 — 8y
0
Got it. Now the only problem I need to solve on my own is how to work this into my standard OOP system I've established :P randomsmileyface 375 — 8y
Ad

Answer this question