Do I HAVE to include metatables in my games?

With my game development history, I have NEVER used metatables once as I do not fully grasp the concept. But speaking to bigger developers, I found that 9/10 of them use metatables of some sort but despite them explaining it I still do not see a use for it (mainly because I do not understand it). Would I eventually have to learn metatables if I wish to create a polished game? If so when would I have to use it and for what purpose.

2 Likes

Metatables are just a way to extend your general tables, aka you can make it so you can call a table like table("hi") or be able to tostring a table. But they are not required to make your game polished, they just make your life a bit easier if you know what you need.

I suggest reading this Developer Article if you want to learn more on Metatables.
https://developer.roblox.com/en-us/articles/Metatables

2 Likes

No

There isn’t only one way to make a polished game. If meta-tables aren’t really your thing, then don’t feel like they are necessarily essential to be added into a game. One great thing about scripting is that there are so many ways to accomplish the same result in different ways, styles, and fashions. It’s possible that in certain scenarios meta-tables might make things easier, but at the same time there are also certainly situations where they aren’t that helpful. Nonetheless, don’t feel boxed in as if your game design has to be constructed a certain way for it to be “good” or “polished”.

11 Likes

Nice to know that it’s not necessary to achieve such a polished using metatables, thank you!

Putting advanced topics aside (such as inheritance). I use metatables when I was to define objects. Things like… an Item.

Lets say we store Items in a table. local Items = {}

Well sure that’s where the items go but now we need to make an item. Well that too is a table.

local ItemClass = {}

This item is looking kind of bland isn’t it? (Do note the difference between these two tables is Items is the table we store references to our objects in. While ItemClass is the actual class that gets copied when making a new instance of an Item)

Lets give our item some stats.

local ItemClass = {
  Name = "Default"
}

Now our Item has a name. But its still not an object yet. Lets set up a metatable.

local Item_MT = { __index = ItemClass }

That’s a cool metatable right? We indexed the method __index which is used whenever you look up an index in a table. ItemClass[Property] or ItemClass.Property both of these methods call the internal __index function in Lua.

Now lets make a new item.

local ItemInstance = setmetatable( {}, Item_MT )

Now we have a new item. Isn’t it kewl. If we did print(ItemInstance.Name) it would print "Default"

Let’s wrap this in a function and clean up our code to see a finished product.

local Items = {}
local ItemClass = {
  Name = "Default"
}
local Item_MT = { __index = ItemClass }

local function NewItem(Name)
  local ItemInstance = setmetatable({}, Item_MT)
  if Name then ItemInstance.Name = Name end -- If we passed a name lets set it.
  Items[#Items+1] = ItemInstance -- Add the instance to the table so we can keep track of it.
  return ItemInstance
end

local BottledWater = NewItem("BottledWater")

if BottledWater then
  print(BottledWater.Name)
end

This is really powerful because you aren’t limited to strings only, you can also put functions, tables and other data types inside of the Item table. If you search our new "BottledWater" object for a value that doesn’t exist in the new table ( the brackets inside of the setmetatable() function) then it will search the ItemClass table for it. If its not there then it will return nil.

6 Likes

You don’t need metatables for game development at all and they are usually used when a person tries to OOP in lua. I personally am against OOP in lua and would not recommend it.

just because OOP is popular in roblox doesn’t mean it is an actual good programming paradigm; there are countless videos on why OOP is overrated and wrongly worshiped

4 Likes

I couldn’t have explained it better, good job on taking your time to explain it!
Metatables are also really useful when you do OOP, Object Oriented Programming.
OOP allows you to make your very own Classes, and you can read more about OOP if you want here: (just dont do OOP until you learned a bit of metatables)

2 Likes

I understand a lot better now, thank you for taking the time to elaborately explain this! :smile:

1 Like

Not a long time ago, someone made a post(not visible for new members) and showed why OOP is bad.

Be sure to use them correctly, otherwise it is overcomplicated.

1 Like

Haha, so many hate OOP. :confused:
Don’t throw out the baby with the bath water though.
There’s a difference between shoving your entire game into OOP schemes and just connecting functionality with state.

Singletons (modules used as Objects aka Tables)
If you want a moduleScript to act as an overarching table for the game with a permanent reference point(no passing around) for what ever purpose, and you want it to have some functions to do stuff where ever that table is but don’t want to run into those functions when you “for all in pairs()” it

Make a second table (moduleScript) with the functions and set it as the first’s metatable.

--module 1
local ServerScriptService = game:GetService("ServerScriptService")
local RosterHandler = require(ServerScriptService.RosterHandler)
local Roster = {}
setmetatable(Roster, {__index = RosterHandler})
return Roster

--module 2
local RosterHandler = {}
function RosterHandler:stuff()
  --do stuff
end
return RosterHandler

TADA! takes bow :stuck_out_tongue:

Two rules of programming

  • do what works
  • do what doesn’t confuse you
    optional third rule
  • try to learn new ways(more efficient or more clear or just cool)
1 Like

Metatables and controller classes can be extremely powerful if you use them in moderation. Instead of strictly considering OOP, consider an Entity-Component system. For example, consider this: You create tables for important game entities such as players, NPCs, and such, and tie them to respective metatables. This allows for much more organization and simplicity than creating generalized functions and master scripts to handle everything. I wouldn’t recommend delving into advanced OOP topics such as inheritance, polymorphism, abstraction, etc, at least not in this context. It can get messy really easily and more than likely won’t suit your purposes in game development. However, do consider metatables as a tool to help you control entities in your game. In the end, its in your best interest to keep your codebase as simple as you can. Metatables can help with that, but if you feel pressured to specifically use them then they’ll have an adverse effect on your development.

2 Likes

So could you compare module scripts to metatables? I have scripts that require modules which hold various functions.

You don’t have to use metatables no, but you have to grasp what they are. If you’re building entire games in a language and you don’t understand a part of it as big as metatables, you’re going to have a bad time.

For what its worth, I like OOP. I like that way of thinking about things. It has its flaws, sure. People who are like GARGHHHHHH DEATH TO OOP are just taking a radical position because its so many people disagree with them, don’t take them too seriously. In fact, anyone who says an entire field of very smart, well researched people are totally wrong, probably have a bee under their bonnet

1 Like

No this comparison doesn’t make sense, they’re two distinct entities, one is a functionality in Lua the other is a way of organising code.

You can use a module script to replicate some of the functionality of metatables, but they aren’t alike. I really recommend researching metatables

1 Like

Many people learn about metatables and then try to apply them to literally everything they possibly can. I’ve seen many people create metatables in their games for seemingly no reason at all. This is the wrong way to use them. This will just confuse you and likely make you end up not wanting to work on your own scripts due to the complicated mess that you’ve created.

You do NOT have to use metatables at all in order to have a well polished game. In fact, it may hinder your polishing as people tend to fall into a trap when it comes to metatables and unnecessarily complicate everything.

If you have a fair understanding of how they work and need to use them to create more powerful tables, then knock yourself out. Otherwise, don’t waste your own time. You’ll get by just fine using other methods.


I’ve just coded an example as to when I may use metatables, click below to view the code.

Metatable Example Code
local proxy		= {};
local methods	= {};

local myTable = setmetatable({}, {
	__index = function(self, index)
		return proxy[index] or methods[index];
	end;
	
	__newindex = function(self, index, new)
		if (rawget(self, "listening")) and (self.listening[index]) then 
			self.listening[index](proxy[index], new);
		end;
		proxy[index] = new;
	end;
});

function methods.addListener(variable, callback)
	if not rawget(myTable, "listening") then 
		rawset(myTable, "listening", {});
	end;
	myTable.listening[variable] = callback;
end;

-----------------------------USING THE TABLE-----------------------------

myTable.x = 0;

myTable.addListener("x", function(old, new)
	print("x was changed from ".. old .. " to ".. new);
end);

for i = 1, 100 do 
	myTable.x = i; -- prints "x was changed from y to z" with y being the old value and z being the new value.
	wait(1);
end;

--[[
	OUTPUT
	
	x was changed from 0 to 1
  	x was changed from 1 to 2
 	x was changed from 2 to 3
 	x was changed from 3 to 4
	...
]]

I wouldn’t use this to keep track of just one variable, but I personally prefer something like this over creating a ton of IntValue’s and using .Changed or :GetPropertyChangedSignal().

2 Likes

No, my example uses module script functionality to make a meta table for another table.

Modules run once.
They return a table.
Subsequent calls use the cached version (returned table) and don’t rerun the code.

The meta table info required for this is small
it uses the _index operation to reference the table of functions when it can’t find it in the main table.

so you can use a single module to make a single table instead of burying it in a script somewhere.

1 Like