How do I wrap an object/instance in a ModuleScript to create custom methods/functions for said instance?

Hello!

I’m looking to create some custom functions/methods for certain instances (such as player:AwardWin() and things like that). How would I go about doing this?

I’ve tried looking it up but I found some methods involving metatables and __index which I don’t understand at all.

I am okay with having to require the module in each script, but I just don’t understand how I would go about creating those custom methods for instances.

I have some knowledge with using ModuleScripts such as creating functions within the module, but I don’t understand much more than that.

We can do this by using metatables. Metatables allow us to override certain operations on tables, the most common ones being __index (called every time something tries to get an index in the table that does not exist) and __newindex (called every time something tries to set a value to an index in the table that does not exist).

local Proxy = {}

function Proxy:AwardWin()
    print(self, " has won!")
end

function wrapInstance(object)
    local metaTable = {
        __index = function(self, index)
            if Proxy[index] then
                return Proxy[index]
            end

            return object[index]
        end

        __newindex = function(self, index, value)
            object[index] = value
        end
    }

    return setmetatable({_object = object}, metaTable)
end

We can implement this into a module by simply making the module return the wrapper function.

6 Likes

Thanks for the quick response!

I’m still a bit confused as to what a metatable actually is. How is it different than a normal table?

Also, do I have to add return Proxy at the end, or does the return setmetatable do that automatically?

Finally, when you say print(self, " has won!"), is it calling the wrapInstance function each time?

setmetatable will return the table that it set the metatable on, so it’s just a more concise way of saying

local object2 = {_object = object}
setmetatable(object2, metaTable)
return object2

Don’t return Proxy, that’s not the right thing. You’d be returning the same table every time you called wrapInstance.

A metatable is just a container for functions that will be run when a table is indexed, or called, or added or subtracted or multiplied with etc.
If you wanted to make doing, for example, AllPlayers.Health = 50, then you’d need a table with a metatable, and the metatable’s __newindex is a function that finds every player and sets their Humanoids’ health to something.

local AllHumanoids = {}

local metatable = {}

metatable.__newindex = function(tbl, key, value)
	for _,v in ipairs(get all humanoids in the game here, not writing that myself) do
		v[key] = value -- if you did AllHumanoids.Health = 40, then this would be "v.Health = 40"
	end
end

setmetatable(AllHumanoids, metatable)

AllHumanoids.WalkSpeed = 50
print(AllHumanoids.WalkSpeed) -- prints nil, because it was never set, the function didn't rawset(tbl, key, value)
5 Likes

Thanks for the response! I understand now haha, thank you.