Custom events with Oop?

So I want to make a custom event.

For example:

local Family = {}
Family.__index = Family

function Family.new(Name)
	local self = {}
	self.Name = Name
	self.Age = 0
	self.LifeExpectancy = math.random(60,90)
	
	
	setmetatable(self, Family)
	
	return self
end

function Family:AgeUp(Years)
	self.Age += Years
	print(self.Age)
	if self.Age >= self.LifeExpectancy then
		print("Is older than life expectancy")
		-- fire event here
	end
end

return Family

Then in a script:

local Family = require(game.ServerStorage:WaitForChild("Family"))

local Child = Family.new()

Child:AgeUp(100)

Child.Died:Connect(function()
	print(Child.Name .. " has passed away.")
end)

I have tried using bindable events like this:

local Family = {}
Family.__index = Family

Family.Died = Instance.new("BindableEvent")

function Family:AgeUp(Years)
	self.Age += Years
	print(self.Age)
	if self.Age >= self.LifeExpectancy then
		print("Is older than life expectancy")
		self.Died:Fire()
	end
end
Child.Died.Event:Connect(function()
	print(Child.Name .. " has passed away.")
end)

But it didn’t print.

1 Like

Maybe you should create the bindable event in the constructor? If you use the metatable’s event then the event will fire for every instance of the object, which might not be what you want.

He’s trying to use a bindable event to create a custom event for his object the same way a part has events like Changed, or Touched.

What I do is put a reference to the event in the object when it is instantiated, and an optional reference to the bindable’s actual event (to make it look more polished, that way you can do “object.SomeEvent:Connect” rather than “object.SomeEvent.Event:Connect”)

This code worked when I tested it:

local object = {}
object.__index = object

function object.new()
	local newObject = {}
	local event = Instance.new("BindableEvent")
	newObject._EventRef = event -- only accessed internally, not needed by the user
	newObject.SomeEvent = event.Event -- what the user binds their listeners to
	
	return setmetatable(newObject, object)
end

function object:DoSomething(...)
	self._EventRef:Fire(...)
end

local testObject = object.new()

testObject.SomeEvent:Connect(function(...)
	print("Event was fired with args:", ...)
end)

testObject:DoSomething("a", "b", "c")
1 Like

2 posts were split to a new topic: (Web Dev) next to your name (Private Message)

I did that but it still didn’t work:

local Family = {}
Family.__index = Family


function Family.new(Name)
	local self = {}
	self.Name = Name
	self.Age = 0
	self.LifeExpectancy = math.random(60,90)
	
	self.DiedEvent = Instance.new("BindableEvent")
	self.Died = self.DiedEvent.Event
	
	setmetatable(self, Family)
	
	return self
end

function Family:AgeUp(Years)
	self.Age += Years
	print(self.Age)
	if self.Age >= self.LifeExpectancy then
		print("Is older than life expectancy")--It prints
		self.DiedEvent:Fire()
	end
end

Script:

local Child = Family.new()

Child:AgeUp(100)

Child.Died:Connect(function()
	print(Child.Name .. " has passed away.")--Doesn't print
end)

This may not work depending on how you want the event to be used, but you could use a function call instead of an event when the member passes away, something like:

function Family:AgeUp(Years)
  self.Age += Years
  print(self.Age)

  if self.Age >= self.LifeExpectancy then
    print("Is older than life expectancy")--It prints
    self:Died()
  end
end

--Function called when the Family member passes away
function Family:Died()
  print(self.Name .. " has passed away.")
end

This just seems like a safer object-oriented solution, because you’d be manipulating the object within one of its own functions, and better protecting its data.

If you need to know that the Family member has died outside of the object, you can still use a bindable event, but it would require a bit more reworking.

1 Like

Try calling AgeUp after creating the connection and it should work. You’re calling AgeUp and then waiting for it to be called, which doesn’t make much sense.

3 Likes

there are modules for this, since BindableEvents do not associate with OOP

here’s a short one i typed up here, which might not work since i haven’t tested it yet

local SignalStatic = {};
SignalStatic.__index = SignalStatic;

local ConnectionStatic = {};
ConnectionStatic.__index = ConnectionStatic;

function SignalStatic.new(signalName: string): Signal
     local signalObj: Signal = {
            Name = signalName,
            Connections = {},
            YieldingThreads = {}
     }

     setmetatable(signalObj, SignalStatic);
     return signalObj;
end

local function NewConnection(sig: Signal, func): Connection
     local connectionObj: Connection = {
            Signal = sig,
            Function = func,
            Index = -1
     }

     return setmetatable(connectionObj, ConnectionStatic);
end

local function threadFunc(func: any, args: GenericTable, handlerName: string)
     local thread = coroutine.create(function()
          func(unpack(args));
     end)

     local success, err = coroutine.resume(thread);

     if not success then
          error(err);
     end
end

function SignalStatic:Connect(func): Connection
     local connection = NewConnection(self, func);
     connection.Index = #self.Connections + 1;

     table.insert(self.Connections, connection.Index, conenction)

     return connection;
end

function SignalStatic:Fire(...)
     local args = table.pack(...);
     local allCons = self.Connections;
     local allThreads = self.YieldingThreads;

     for index = 1, #allCons do
          local connection = allCons[index];

         if connection.Function ~= nil then
              threadFunc(connection.Function, args, connection.Signal.Name);
         end
     end

     for index = 1, #allThreads do
          coroutine.resume(allThreads[index]);
     end
end

function SignalStatic:Wait()
     local args = {};
     local thread = coroutine.running();
     table.insert(self.YieldingThreads, #self.YieldingThreads + 1, thread);
     args = { coroutine.yield() };
     table.remove(self.YieldingThreads,  #self. YieldingThreads);

    return unpack(args);
end

function ConnectionStatic:Disconnect()
     self.Function = nil;
     self.Signal = nil;
     self.Index = -1;
     
     setmetatable(self, nil);
end
3 Likes