Hey, anyone who is reading this my name is sharafzada (username ofc xD) and I’m an intermodate scripter. I do want to try and break into the advanced part of LUAU and i habe a few questions that I’d likes to be answers and of you guys want drop some tips as well
Why are variable names shorted to 2 characters or less?
How can I use coroutines and task functions better?
How can I use metamethods to my advantage?
I do thank wveryone who decides to read my post or even leave a comment and hopefully with time I will get better
This is usually just a sense of style, as the length of your variables too my knowledge dont change anything other than how organized your code is.
I also dont think shortening variables is considered advanced, in most cases it can be considered unadvanced, especially when working with teams cause who would know what the variable RS is, is it RunService? ReplicatedStorage? Something else?
You cant really tell, thats why its usually better to actually name your variables what they are, obviously you can shorten them, but as long as you can tell what they are.
Metatables are really only useful if you need absolute control over a table, or want some extra capabilities, otherwise you dont really need them that much, and can sometimes be best to just not even use them.
But if youre making like open source stuff, or trying to fool proof something, or just need more control, thats really when metatables shine imo.
I dont have anything to say regarding coroutines and task functions other than just read the docs and try to figure out when you should use em like that, yup :D
I gotta stop myself before I start ranting again lol
hope this helps a bit!
I was like 75% of the way through writing this when FroDev posted the reply so there’s probably a bunch of redundant stuff but anyways:
Please don’t shorten variables to this short. It’s not professional. It’s just mathematicians trying to make stuff impossible to understand. I sometimes use extremely long variable names, and while it takes slightly longer to type, it makes my code much more readable.
As long as you follow regular good optimization practices (no useless code running forever), there’s no real “better” way to use coroutines. Task functions are just coroutines with ease of use added upon them. I recommend using coroutines only if a task function isn’t available.
Metatables are very useful for complex systems, especially with non-physical objects. For example, I’m currently working on a project that utilizes chunks. Metatables are very useful for being able to edit chunks with better readability:
Metatable
chunk:UpdateData(...)
Regular
Chunk.UpdateData(Chunk.chunks["<have to index the table here>"])
Basically, think of metatables as a way to store data for objects such as chunks that don’t physically exist in your game.
I’m sure there’s some other uses for metatables but this is what I personally use them for.
[spoiler]Also Yarik I know you’re gonna argue against my metatable usage but you don’t need to start a full on debate, please…[/spoiler]
Be aware that at this point in your scripting journey I think that its best to stop following tutorials and to start just learning by experience whenever you come across something you’ve never known how to do.
The shorting of variables now does make sense to me xD. I did think there was a reason but now I have I will probably stick to longer names just to understand it better. I have been messing around with metamethods and maybe a bit of task functions not to sure in this come xD but I wouls like to see if you have any general recommendations. Or even tips on how to improve the current core and the code I will write in the future
Variable names are subjective, but it should describe what it is being used for and what is being stored in it. It also shouldn’t confuse any other variables. There’s also a few styles you could write them in, and you can apply your own meaning to them. Just be consistent!
camelCase (I personally use for private methods and constructors)
PascalCase (I personally use for object methods, constant instance variables)
UPPER_CASE (I personally use for constant calculated variables)
lowercase (only for single letter/word variables like i and v)
There’s also this other convention:
_variableName (Use any style, but the _ represents a “private” field)
_ (Represents an unused variable that must be defined for some reason)
I won’t comment on the other two questions since the other replies have covered them, but variables should always be named in a way that is a) easy to digest and b) makes sense to other individuals.
Some rules for following proper naming convention which are applicable across most languages:
Treat variable names as case-sensitive whenever possible (myVariable versus MYVARIABLE)
Variable names should not use reserved names or keywords (such as int/integer, string, etc.)
Constant names should be CAPITAL
Variable names should otherwise be CamelCase or snake_case, or some combination as long as consistency is followed
Limit reuse of variable names (exceptions can include scope and iterator/counter variables)
Keep variable name length dependent on context (for example, iterator or counter variables do not need to have complex names)
Correct me if Im wrong, but I dont think thats actually metatables.
metatables are set up by using setmetatable as you probably know, and then provided with a table that contains functions that depicts how certain aspects of the table works.
What you mentioned just seems like regular ol tables, because with functions that are in tables, if you make the first parameter self, then itll do as you said, or you can just do this:
function Table:FunctionName()
end
So yeah Im pretty sure this is just how tables are ig?
I wanted to look into it more, and I think the best example of what an actual metatable is can be seen via the docs for said metatables:
It does help a bit but I do think it’ll go a long way xD. But, I will take a look at coroutines and the task documentations I do use task functions but peobably not in the right way xD. The metamethods do make a lot more sense now I have been using them ofc but I just didnt really get them as well but now this does clear it up (sorry for the late replies typing on mobile in bed is hard )
Ah, well I will look into that a bit more ofc I dont take it as shame were all humans (I hope xD) and we make mistakes sometimes thsnks for pointing it out
I do feel like yes it really is kind of up to you as I just do it slmost similar to you’re way of how you have it. But overall yes the naming scheme it is super subjective and I will probably just stick to how I have it.
Um why?
All you need to know is: coroutine.resume → resumes thread coroutine.running() → grabs current thread
Scripts is also a thread so coroutine.running() can return script thread
For rest i recomend using task library instead. task.spawn, task.defer, task.delay can also accept threads and not just functions.
It’s best practice to spell out words fully. Abbreviations generally make code easier to write, but harder to read. Following common naming practices when naming your variables can help you and others understand their meaning or purpose:
Use PascalCase names for class and enum-like objects.
Use PascalCase names for all Roblox APIs. camelCase APIs are mostly deprecated.
Use camelCase names for local variables, member values, and functions.
Use LOUD_SNAKE_CASE names for local constants (variables with values that you don’t expect to change).
Don’t capitalize entire acronyms within names. For example, write aJsonVariable or MakeHttpCall.
The coroutine and task library provide functions to create threads that allow you to schedule code to run when another bit of code completes its execution or yields, it’s important to note that this is not multithreading.
There are specific use cases for them, and I would need to know more about what you are trying to accomplish to give you better info.
Metatables/methods are useful for giving functionality to tables, they’re often used in OOP-like modules to point to a schema and provide convenient methods (object:Method).
There are situations where they make sense and where they don’t.
For example, if you’re doing some kind of counter object, you can implement the __add metamethod to reduce verbosity and make your code easier to read and maintain:
local counterMeta = {
__add = function(self, otherCounter)
return self.count + otherCounter.count
end
}
local counter1 = setmetatable({count = 1}, counterMeta)
local counter2 = setmetatable({count = 3}, counterMeta)
print(counter1 + counter2)
You can of course just make a function to index and add count, but that can get tedious and hard to maintain, and metatables are pretty fast as long as you follow good practices (such as avoiding __index functions).
A good example where they don’t make sense is the default Roblox PlayerModule. Don’t try to implement metatables for the sake of “OOP” when you really don’t need it.
The most recent example is where one of my old functions was running forever in some cases, and to fix it I wrapped the function in a coroutine and closed the coroutine when the function had not finished after 2 seconds.
This way the function succesfully stops running and the rest of your code will run without error. It’s an old script in a somewhat closed system, so that is why I prefer using that quick solution instead of adressing the core problem of the function not finishing on its own. Sometimes you got to recognize what solutions are best for your current situation.
That is why coroutines are really powerful. You get to control a thread. It is very useful for resetting character states and procceses when they suddenly stop doing that process (fx. when a player unequips their fishing rod while fishing, you can close the entire fishing thread by wrapping it in a coroutine).
I think the others already made it pretty clear. I’d also say this is kind of a newbie mistake (low blow to youtubers ). With autocomplete,even without AI, there’s really no excuse not to use full, descriptive names.
Understanding them deeply.
A coroutine is a piece of code that can be paused. That’s super valuable and lets you solve some pretty tough problems in simple ways.
When it comes to tasks, the key is to understand the task scheduler. I recommend only reading the official Roblox documentation. everything else is either unreliable or just ends up confusing you.
My advice? Just don’t use them.
When you use metamethods, you’re basically altering the language (that’s called metaprogramming). The main reason metaprogramming isn’t that popular is because it makes code hard to read and maintain. You can look into it if you’re curious.
Also, anything you can do with metamethods, you can do with regular code, including OOP:
local Chunk = {}
function Chunk.New()
local chunk = {}
chunk.IsCool = false
--methos
chunk.MakeItACoolChunk = Chunk.MakeItACoolChunk
return chunk
end
function Chunk:MakeItACoolChunk()
self.IsCool = true
end
local chunk = Chunk.New()
chunk:MakeItACoolChunk()
print(chunk.IsCool)
Some other tips:
Learn strict typing (and generic programming if you wanna go deeper)
Learning Unity is super valuable. there are tons of solved problems, awesome algorithms, and a lot of stuff you can apply to Roblox. Honestly, that really changed my life.
This isn’t really true.
You can re-implement things without metamethods, but it actually makes the code more clunkier and actually less maintainable.
Just as an example:
local mt = {
__tostring = function(obj)
return "(" .. obj.x .. ", " .. obj.y .. ")"
end
}
local t = setmetatable({x = 1, y = 2}, mt)
print(t) -- Output is "(1, 2)"
In this case, you don’t have to remember a custom function call every single time, which is just annoying, you can literally call print() and pass t. (There are plenty more cases where using metamethods is useful - e.g., __concat, __index, __add etc.).
So while you can somewhat simulate the same higher-level functionality (like OOP or method dispatch) with normal code, you can not replace the integration into Lua’s operators and syntax without metamethods.
I’ve never had to use promises in my code, but there are cases where they could actually be useful. I don’t think it is a total waste of time if you use them correct.
They’ve probably never had to go back months later and understand their code. Short names are usually for convenience, but the autosuggestion features most ides have make it almost as convenient to use long descriptive names anyways so always favor a descriptive name.
Coroutines just effectively make it so your code can run at the same time as other parts of your code. Since they don’t actually go across multiple cores it isn’t a speed benefit, so it only is useful for keeping logic separated where it makes sense. In a lot of cases that will be around yielding differences or just clarity. So long as you aren’t overcomplicating functions trying not to use coroutines, you’re probably fine tbh. Just make sure you don’t create a bunch of infinite ones with a leak that never cleans them up of course. (If you want more speed, parallel luau is the way to go since it does use multiple cores, but they are a bit more complex to use)
The answer is most cases they won’t help you. Unless it’s a very well defined format like how people fake oop, they don’t get a ton of use because they often aren’t actually needed. So long as you know what meta methods are available most problems where they can help will generally be clear when you are on the problem, but honestly you can just ignore them completely and be fine.
One of the very few cases where the problem could clearly benefit from a metatable for me was my debug scripts. I basically use an OOP like style for handling calls, but instead of actually calling those functions, I simply record them and their arguments that the scripter wanted into a list so I can play them back whenever I want. It turns a direct mode instruction into a command list.
example code
the full code is a bit more annoying, but this is the core of it’s behavior. When a user uses my debug tools (this is mostly for actually drawing things for geometry debug or other visual debugs) they just record what information they would want to be displayed and my debug tools create a stepper tool that allows you to walk through all of them in any order.
local DebugStage = {}
DebugStage.__index = function(tbl, index)
local command = rawget(basicDebugFunctions, index)
if command and typeof(command) == "function" then
return function(...)
tbl:RecordCommand(index, ...)
end
end
return command
end
This is it.
Systems programming fundamentally differs from game logic programming.
I rarely use OOP/Metatables when scripting actual game mechanics, but I almost always use metatables and OOP when scripting libraries/frameworks where it makes sense.
TL:DR
Keep game logic lean, while leveraging OOP for any infrastructure code.