hey guys, I have been working with OOP a lot lately and have found myself making custom events a lot, like Car.Stopped, for example. As it is now, the usual approach for this sort of thing is to just make a bindable event. I don’t like the idea of making a new bindable event and having the responsibility of managing it, so I created a custom signal class that does not use bindable events. It has a few other upsides:
It does not use any sort of repeat loops to yield, it uses coroutines and the task library.
metatables can be passed, making it more flexible than current RBXScriptSignals
room for new features
in my testing, it has perfect performance compared to the usual bindable event method
you can find it here:
it has everything current RBXScriptSignals have, including :Wait(), :Connect(), and connections can be disconnected with :Disconnect().
I would love feedback on memory management here, I don’t know the best approaach for a dedicated :Destroy() function. the current one sets the entire signal to an empty table, which is good enough for me considering it will be gc’ed anyways, but I know some people might not be happy with that.
edit:
the great thing about this is that I can add features that I’ve always felt were missing from RBXScriptSignals. for example, a timeout to :Wait(). ive actually just added that because I feel that its an important thing to have
Can you upload the source code into GitHub (either gists or a repo) or make a pastebin or include it in your post? So that peopl who cannot access Studio will be able to read the source code.
-- CreateSignal.lua
--[[
__ __ __
____ __ __/ /_/ /_____ / /_ ______ ___
/ __ \/ / / / __/ __/ __ \/ / / / / __ `__ \
/ / / / /_/ / /_/ /_/ /_/ / / /_/ / / / / / /
/_/ /_/\__,_/\__/\__/\____/_/\__,_/_/ /_/ /_/
RBXScriptSignal Replacement
functionally identical!
~EXCEPT~
this can pass metatables too
not SUPER useful but maybe you might find a use for it
to install:
put somewhere you can use it (usually ReplicatedStorage is the best place for this)
use:
local signalClass = require(game.ReplicatedStorage.signalClass)
make a new signal:
local itHappenedSignal = signalClass.new()
wait for the signal to fire:
itHappenedSignal:Wait()
print("it happened!")
wait returns arguments:
itHappenedSignal:Fire("hi")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
print(itHappenedSignal:Wait())
(will print "hi")
connect and disconnect:
function happen()
print("it happened")
end
local con = itHappenedSignal:Connect(happen)
con:Disonnect()
the main use for this is for things like OOP classes:
function Car.new()
return setmetatable){
Stopped = signalClass.new()
}, Car)
end
function Car:Stop()
self.Stopped:Fire()
end
~~~~~~~~~~~~~~~~~~~~~~~~~
myCar.Stopped:Connect(function()
print("my car stopped!")
end)
]]
local Signal = {}
Signal.__index = Signal
local Connection = require(script.Connection)
function Signal.new()
return setmetatable({
_Connections = {},
_Yielding = {},
Firing = false
}, Signal)
end
function Signal:Fire(...)
for i,Connection in ipairs(self._Connections) do
if Connection._Callback == nil then table.remove(self._Connections,i) else
spawn(function(...)
Connection:Fire(...)
end)(...)
end
end
for i,thread in pairs(self._Yielding) do
table.remove(self._Yielding, i)
task.spawn(thread, ...)
end
end
function Signal:Wait(duration)
local Running = coroutine.running()
table.insert(self._Yielding, Running)
if duration then
task.delay(duration, function()
local index = table.find(self._Yielding, Running)
if index then
table.remove(self._Yielding, index)
task.spawn(Running)
end
end)
end
return coroutine.yield()
end
function Signal:Connect(callback)
local connection = Connection.new(callback)
table.insert(self._Connections, connection)
return connection
end
function Signal:Destroy()
self._Connections = nil
self._Yielding = nil
self.Firing = nil
setmetatable(self,{__index = function() return nil end})
end
return Signal
-- Connection.lua
local Connection = {}
Connection.__index = Connection
function Connection.new(callback)
return setmetatable({
_Callback = callback
}, Connection)
end
function Connection:Fire(...)
self._Callback(...)
end
function Connection:Disconnect()
self._Callback = nil
setmetatable(self,nil)
end
return Connection
I got a question, how to make a custom type that accepts outputs from Signals.new()? Since right now for typechecking I am using RBXScriptSignals but I’m not sure that’ll work.