Problem
When writing OOP modules we often want to add signals such as object.Changed, object.Completed etc. To write these you usually do:
local event = Instance.new("BindableEvent")
self.OnComplete = event.Event
self._onCompleteEvent = event
and in your code you fire it by doing
self._onCompleteEvent:Fire()
This gets very messy!
Solution
Instead add these two functions:
function Object:CreateEvent(name)
local event = Instance.new("BindableEvent")
self["_"..name] = event
self[name] = event.Event
end
function Object:FireEvent(name, ...)
self["_"..name]:Fire(...)
end
Example code:
self:CreateEvent("OnComplete") --Create the signal
self:FireEvent("OnComplete", 1, "2", true) --Fire the event
Personally recommend building your own implementation of events if you are using it like this, That way you get around the annoyance of having store the bindable and the bindables event and makes it easier to clean stuff up from outside the class
Why not just build custom events at this point… e.g.
local function CreateEvent()
local EventClass = {}
function EventClass:Fire(...)
return EventClass.Callback(...)
end
function EventClass:Connect(Callback)
EventClass.Callback = Callback
end
return EventClass
end
local Event = CreateEvent()
Event:Connect(print)
Event:Fire("Hello world") -- Hello world
(this is also automatically garbage collected, like instances are when there are no references left attached to them)
Your eventclass isn’t asynchronous and doesn’t support multiple listerners.
How would that look?
You are going to have to save either a bindable or a function that triggers the event. Eitherway there’s gonna be two variables stored in your class.
-- Asyncronous version:
local function CreateEvent()
local EventClass = {
Callbacks = {}
}
function EventClass:Fire(...)
for Callback in pairs(self.Callbacks) do
task.defer(Callback, ...)
end
end
function EventClass:Connect(Callback)
self.Callbacks[Callback] = true
local EventConnection = {}
function EventConnection:Disconnect()
self.Callbacks[Callback] = nil
end
return EventConnection
end
return EventClass
end
local Event = CreateEvent()
Event:Connect(print)
Event:Fire("Hello world") -- Hello world
An asyncronous and multiple listeners event…
(also automatically gets garbage collected)
sidenote: there is a much more efficient way to code this from a memory perspective, this is just to follow the standards people are used to with events, i personally tend to avoid using as many tables as this to hold functions and things.
From the benchmarks I completed when testing my personal event class, if you can get the event down to just being a table containing the connected functions its ~2x more memory efficient then using bindables.
These custom event classes also allow more configuration (e.g. firing synchronously and asynchronously for performance, connecting a callback for when all connects to the event are removed so you can clean it up etc.)
You are going to have to save either a bindable or a function that triggers the event. Eitherway there’s gonna be two variables stored in your class.
I think Artoshia basically explained this, But yeah if you are just directly allowing connecting and firing the event then you are only storing one instance
I think I’ll end up using @Artoshia solution, it’s very convinient to be able to do:
self.OnChanged = Event.new()
and then just call and connect to the same variable.
For anyone reading, here’s the modified version I’ll be using:
--Credits to Artoshia
local Event = {}
Event.__index = Event
function Event.new()
local self = setmetatable({}, Event)
self.Callbacks = {}
return self
end
function Event:Fire(...)
for Callback in pairs(self.Callbacks) do
task.defer(Callback, ...)
end
end
function Event:Connect(Callback)
local callbacks = self.Callbacks
callbacks[Callback] = true
local EventConnection = {}
function EventConnection:Disconnect()
callbacks[Callback] = nil
end
EventConnection.Destroy = EventConnection.Disconnect
EventConnection.disconnect = EventConnection.Disconnect
return EventConnection
end
Event.connect = Event.Connect
return Event
EDIT: Did small tweaks and fixed the disconnect bug.
More featureful implementations of custom signals can be found literally everywhere on the forum, for instance: Lua Signal Class Comparison & Optimal `GoodSignal` Class
Methods like DisconnectAll, or just the Wait method can be very useful.