local tab = {}
local functions = {
__index = function(self,value)
print(self.StringValue,Value)
end
}
setmetatable(tab,functions)
tab.StringValue = "Test"
local new_index = self.New
So would this now print “Test”, “New”?
local tab = {}
local functions = {
__index = function(self,value)
print(self.StringValue,Value)
end
}
setmetatable(tab,functions)
tab.StringValue = "Test"
local new_index = self.New
So would this now print “Test”, “New”?
Yes, except you aren’t closing your table with a closing bracket.
Thank you man,
Btw, using self
only works when working with metatables?
Hey there @7z99, I have a question. I noticed when making my version of @Valkyrop’s Custom Functions thingy, if I try to use a built-in workspace method (for example FindFirstChild
) I get an error:
Expected ':' not '.' calling member function FindFirstChild
Here is what I got for the module:
local realWorkspace = workspace
local myWorkspace = {}
function myWorkspace:__index(key)
if myWorkspace[key] then
return myWorkspace[key]
else
return realWorkspace[key]
end
end
function myWorkspace.new()
local newWorkspace = {}
return setmetatable(newWorkspace, myWorkspace)
end
function myWorkspace:GetItems(class)
local items = {}
for _, item in realWorkspace:GetDescendants() do
if item:IsA(class) then
table.insert(items, item)
end
end
return items
end
function myWorkspace:Loop(callback)
local items = {}
for _, item in realWorkspace:GetDescendants() do
callback(item)
end
return items
end
return myWorkspace
and my script to call it:
local custom = require(game.ServerStorage.CustomFunctions).new()
local parts = custom:GetItems("BasePart")
print(parts)
custom:Loop(function(item)
print("Current Item", item)
end)
local baseplate = custom:FindFirstChild("Baseplate")
No, self just refers to the object that you called a method on. It’s essentially a predefined variable that isn’t always visible when calling a function with a colon.
local t = {}
function t:something()
end
t:something()
is the same as doing
local t = {}
function t.something(self)
end
t.something(t)
It’s just that calling the function with a colon is easier to read.
I found a really good tutorial explaining OOP
in roblox, thats how I learned about metatables (a little, I didn’t finish the whole thing)
Oh, thats nice.
So basically we can refer to self/use it for every table we have
Like:
local blocks = {
Grass = 10;
Gold = 20;
{
print(self.Grass)
That’s because you’re trying to call a workspace function on a table, to fix it you would have to wrap the actual instance method with another function like so:
local methods = {}
methods._extends = workspace -- the "original" instance, this would be constant throughout all of your workspace extensions but if your instance can vary, you will want to define this in your constructor below
function methods:__index(key)
-- remember 'self' is already defined
-- case 1, the function exists in the methods table
if methods[key] then
return methods[key]
else
-- save the member to a variable for readability
local member = methods._extends[key]
-- if your instance can vary and is defined per-object, do self._extends[key] instead of methods._extends[key]
if type(member) == 'function' then
-- case 2: the member is a function
return function(_, ...) -- _ in this case would be equal to self
return member(methods._extends, ...) -- remember to switch to self._extends if the real instance is per-object
end -- now, this function gets called when called which calls `member` with the proper arguments
end
-- case 3: the member is a property, return it
return member
end
end
function object.new() -- you can define 'arg' in here if you're passing a varying instance which gets defined per-object
local newObject = {}
-- if you are going to pass an object here to extend, you'd do something like
-- newObject._extends = arg
return setmetatable(newObject, methods)
end
Not quite, self
isn’t defined here. self
is just a variable really, it just gets defined automatically when a function is defined with a colon
local blocks = {
Grass = 10;
Gold = 20;
}
function blocks:GetGrass() -- remember, defining a function using a colon will assign `self` automatically, in this case, self is blocks
return self.Grass
end
-- also remember that the above function definition is the exact same as doing this:
function blocks.GetGrass(self)
return self.Grass
end
-- now, this will not error:
blocks:GetGrass()
Oh wow, that simple?
Now I understand it.
But why when it comes to stuff like I said in the original post, require more stuff( such as .__index, etc…)
Whereas in this example, 1 simple function and we have “GetGrass” event.
That’s because you’re looking to add onto the existing functions of workspace which you can’t do natively. If you just set the __index metamethod to workspace, you’ll encounter unexpected behaviours or you will be using memory inefficiently.
If __index is just workspace, you will do:
1- Look into the object for that index which should only have properties.
2- Look in the workspace for a member with that index.
Then it ends there.
This isn’t what you want because what happens if we want to add a function? We could define functions per-object but that is not an efficient use of memory because if you have 100 objects, you also would have 100 function objects, when you could have 1 single function object for all 100 objects.
So, bearing that in mind, you want to go:
1- Look in the object for the member (property)
2- Member doesn’t exist in object so we want to look in the methods table
3- Method doesn’t exist in the methods table so we want to look in the instance for the member.
For that reason we want to use a function that goes through all of the members of each involved object, instead of just through two objects.
Remember, in this case, a table’s metatable can be the same for any number of objects.
local t = {}
setmetatable({}, t)
When you do that, the table’s metatable is actually a reference to t
, not just a clone of it.
So if I were to do that:
local Items = Workspace:GetDescendants()
function Items:GetSpecifiedClass(class)
local toSend = {}
for _,item in pairs(self) do
if item:IsA(class) then
table.insert(toSend,item)
end
end
return table.concat(toSend,",")
end
Items:GetSpecifiedClass("Model")
This is supposed to work , but why when I try to make a custom event (to escape the class checker etc), it requires using metatables
Like,
If I had a custom function that will return me only parts with the given class param, it would have been more efficient than this one
From what I understood so far is that metatables can help us when we want to access something that doesnt exist in a table
In this case you would just be getting a table of items from workspace and adding a function to that table. Using an instance wrapper is less efficient than doing what you’re doing but it increases readability and helps with debugging and whatnot.
Usually that is the case, yes. They do also allow for arithmetics to be performed on tables, calling tables and more. Metatables can also be better for memory since it reduces the number of objects (functions) that exist per-object. But if you’re just looking to create a library of utility functions, a wrapper probably isn’t what you want. You can just make a library of functions and that will be more efficient than wrapping an object.
Oh , thanks again man. Appreciate your patience and time!
I’d prob mess with it a bit and update here if needed. Appreicate
Hi again,
So I now have this,
And my goal is to make an event that’d change the properties in that table( must be real properties), what should be changed in order to make this event work properly?
local list = {
Health;
WalkSpeed;
}
function list:GiveSpeed(Target:Model, Speed:number)
Target.Humanoid.WalkSpeed = Speed
end
list:GiveSpeed(Player1,25)
Tho, it doesnt seem like self/ metatables are used here, and this code works. Weird
From this code you can see it might be easy to make custom events / functions, but is that really good practice?
Do you mean a function that does this? I don’t think an event to change properties would be useful here.
You can use self there like this:
function list:GiveSpeed(Target:Model, Speed:number)
Target.Humanoid.WalkSpeed = Speed
self.WalkSpeed = Speed
end
In this case, though, I think going the OOP-route might be more useful for memory and organization. Let me know.
Yeah something like that. Tho,
But isnt self
supposed to represent the humanoid of the model?
No, self would be defined as list
in this case.
Remember,
list:GiveSpeed(Player1, 25)
is the same as
list.GiveSpeed(list, Player1, 25)
Oh, but how would it give the player the speed then?
Also, is it true that if I had this:
local list = {}
local function list:GiveSpeed(....)
--....
end
Is that true to say the function is inside that table?
Because if not, __index could be useful
You’d probably use Target in this case, you’d redefine target to be a player instead of a model.
function list:GiveSpeed(Target:Player, Speed:number)
local character = target.Character or target.CharacterAdded:Wait()
character:WaitForChild('Humanoid').WalkSpeed = Speed
end
Yes, it’s in that table. You can use metatables here though, it would be useful if you’re working with many tables (eg. one table per player) but I’m not sure if you’re doing that.
local metamethods = {}
metamethods.__index = metamethods
function metamethods:GiveSpeed()
-- ...
end
function metamethods.new()
local obj = {}
-- properties/events inside of obj here
return setmetatable(obj, metamethods)
end
return metamethods
You can’t define local functions inside of a table though, you need to define it as function table:func()
, can’t do local function table:func()
, you’ll get an error.
local module = require(path.to.module)
local newObj = module.new()
newObj:GiveSpeed() -- in this case, `self` would be newObj