[solved] Should I use OOP here? If yes, then how?

Hi! I’m currently trying to create a game. In my game I need to make quite a lot of systems, such as inventory, guns, GUI, input, interaction and so on. I already know what OOP is and I’m considering using it in my scripts. Is it a good idea?

Also, if I decide to actually use it then which option is better: put each system in a separate script OR maybe use one script and some modules?

Last of all, I already know what OOP is and how it works. However, I’ve never used it in Lua, so I’m still a bit confused here. I tried learning it, but I’m having trouble understanding all this stuff with metatables and such. What is the best way to learn OOP in roblox lua?

OOP is not necessary if you’re looking for a game without organisation. OOP is really only good to use for organisation, so it’s up to you

2 Likes

What do you mean by ordering?

Sorry, I meant to say organisation there

Well right now I’ve only done a small part of the work so I’m still not sure about how will the organisation turn out. I’d like to be able to modify one of the systems without having to adapt all other ones to it. Would OOP help me with this?

You can do OOP if you like, however I think that if you’re still struggling to figure out how to structure a Roblox game, it’s probably better if you just keep things as simple as possible (e.g. using plain scripts and modules) and go into proper Lua OOP when you have a bit more experience.

OOP is useful for games because they use objects very frequently, however every now and again it can be a drawback as it requires boilerplate and a certain structure that could hinder in you in just quickly making things work.

Whichever you like. In my case I tend to use distinct scripts for “categories” of game things by default, and when I need to share things from these scripts I turn them into modules. For example I have a script for managing players when they join, leave, etc. and how their data loads and that sort of thing, and then I have a script for handling gameplay stuff like validating weapon hits and dealing damage. I also have a PlayerData module that does temporary storage and DataStore things which can be used from any of the scripts on the server. Client-side I often use a single script in a ScreenGui, but if that ScreenGui has lots of different things in it I will divvy it up into modules that get required by the single script. There’s no right way to do all of this, however.

First thing: OOP in Lua is fake. There’s no actual OOP. Metatables and syntax sugar just provide some useful behavior to pretend that a table is an object.

Metatables are tables that allow you to control how another table responds to things being done to it. (That’s why they’re called metatables – they’re tables but for the purpose of tables.)

For example, you can control how a table responds to being part of an addition like 1 + table by putting a specific function inside the metatable to “catch” this (these are called metamethods):

local meta = {
   __add = function(thisTable, valueToAddWith)
      return thisTable.SomeNumber + valueToAddWith
   end
}

What makes metatables really useful, though, is the __index metamethod.

__index gets called when the table is indexed and it doesn’t have a value for that index. Let’s say we have an list table with 3 values: {1, 2, 3}. If we did table[5], well this element does not exist in the table, so __index will get used if it is defined. If we did table[2], this does exist so __index will not be used.

__index = function(thisTable, indexThatWasNil)
    error(indexThatWasNill.. " is not a valid member of thisTable")
end

__index can also be set to another table, so if you had two tables like this:

local table1 = {Damage = 5}
local table2 = {Speed = 3, Name = "Fred"}

local meta = {__index = table2}

setmetatable(table1, meta)

Then you tried to index table1 to find Name, which is nil in table1, __index will redirect that index to table2 instead. So table1.Name will return “Fred”. If there was no metatable, doing this would just return nil (default table behavior).

Note how I slipped in setmetatable() there. It just “applies” a metatable to a table. So in this case I said “meta should be the metatable for table1”.

So how does this go into OOP? What you can do is make a table that defines everything for a class, i.e. all of its properties and methods. Then, when you make a new instance of that class, a new object, you return a new, empty table but set the metatable __index to refer to that class definition table.

local Class = {}
Class.DoSomething = function() end

local newInstance = {}
local newMeta = {__index = Class}
setmetatable(newInstance, newMeta)

newInstance.DoSomething()

So we’re getting close to proper OOP, but a few things are missing. One thing I will not before we continue is that you don’t need to make a separate table to set the metatable, you could just do the following for shorthand:

local Class = {}
Class.__index = Class -- refer to itself
Class.DoSomething = function() end
local newInstance = {}
setmetatable(newInstance, Class)

You can automate this by making a Class.new function:

Class.new = function()
   local newInstance = {}
   setmetatable(newInstance, Class)
   return newInstance
end

Ok so the things we’re missing are self and method calls. In Lua these are actually one and the same – you will see what I mean. Know that both of these things are “fake” however.

You have two OOP things in lua: self and : for method calls (object:DoSomething()).

self is actually just a normal variable, but syntax highlighting pretends it ts a keyword.

-- these are effectively the same
Class.ThisIsAMethodCall = function(self)
   self.Value = true
end
Class.ThisIsAMethodCall = function(myRandomTableVariable)
   myRandomTableVariable.Value = true
end

The colon is more complicated, but still fairly simple. It is syntax sugar in Lua, when you use a colon in Lua what it does under the hood is it just calls the function but adding the table as the first argument.

-- these are equivalent
object:DoSomething()
object.DoSomething(object)

You can also use it when defining functions, if you don’t feel like typing out self manually.

function Class.DoSomething(self)
   self.Value = true
end
function Class:DoSomething()
   self.Value = true -- self is defined invisibly
end

So in Lua to do OOP, you make a table, set the __index metamethod to refer to itself, then when creating a new instance of this class you set the class table itself as the metatable for the new object table. When calling and creating methods you can either use the colon syntax sugar or manually pass in/define the self variable.

This is a ton of information, but hopefully that gets you started on understanding how OOP in Lua works. I think there are a few tutorials about OOP on the DevForum, so check those out.

Maybe a little bit, but not in any miraculous way. Games by their nature are interconnected. What little you can do to keep things separated can be done without OOP; it is merely one method by which you can attempt to accomplish that goal. It’s probably a good start, though.

4 Likes

I mainly use OOP when the game has a lot of content or objects that behave similarly but with small differences. (Like weapons, NPCs, etc.)

OOP is useful when you want to organize huge code bases or want to more easily expand it in the future. I can easily create new modules (classes) and drop them in a folder to create new content for games I work on. You can achieve this with procedural or functional programming too so it’s really just up to personal preference. I come from a Java (Minecraft modding) background so my preference is OOP, but the programming styles have no significant benefit over eachother. The power is in the user, not the method.

It also helps not to overdo it, you don’t need a class for every little function or task.

1 Like