Are practices such as type checking, metatables, and other things such as assert() necessary for more advanced programming? I see a lot of developers use things like these and I’m not sure if it’s something that I should learn in order to advance.
If there are these kinds of practices that exist, please let me know about them as well so I can study them
Luau OOP, asserting types, and using basic existence checks are the best practices.
OOP
local MyClass = {}
local self = setmetatable({}, MyClass)
function self:someMethod()
print(self)
end
return MyClass
This allows us to create classes that have their own special methods, and retain information relating to themselves. It also allows us to preserve the table itself, especially for use in inheritance, and composition.
Asserting Types
Asserting types is extremely important. Roblox’s type checking is extremely rudimentary which is why it’s important to constantly check for ourselves. It’s called basic sanity.
Maids
A maid essentially keeps track of your events (.Touched, .MouseButton1Click, etc) and makes sure they get disconnected properly when needed. This helps to prevent any sort of memory leaks.
ContextActionService
A lot of new developers completely underlook this service, but it’s extremely useful. The CAS allows you to bind and unbind UserInput depending on what is happening. For example: When a player sits inside a vehicle seat you can quickly bind certain actions for that vehicle, and then when the player gets out of the vehicle you can unbind the actions. It is much more useful than using UserInputService for those things.
Promises
Using promises when doing anything related to DataStore, MemoryStore, or HTTP requests is also extremely useful. A promise is quite literally the name. It is a promise to do something with the data, if you don’t do anything with it the Promise gets rejected and all the data is garbage collected.
Watch RDC’s
RDC, or Roblox Developer Conferences, have several extremely important panels. My favorite one which also goes over these specific practices is this one. Generally they have developers from big games, or studios explain how they make their games, and how people can improve upon their own skills.
Code Reviews
Simply put you can easily post your own code in Code Review, this is where many people request for reviews of their own code. You can get some pretty good and insane advice on how to do things properly here.
Thanks for the help. Got a question though: I’ve seen a lot of instances of programmers using metatables, is it generally used for just about everything?
It’s used almost exclusively for the OOP idiom, so the metamethod you’ll use and see 99% of the time is __index (__tostring is helpful, too, sometimes).
Something you should definitely learn and use is event-based programming using bindable events or signals.
Yeah, most of my current work involves using events. I’m just wondering what I can do in order to move on to a more professional level of roblox programming since I’ve been doing it for a while but never been sure what’s good to do.
Got it. How often should I be trying to use OOP? I’m not too sure what the things I should apply it to are. For example, would it be used for something like a module to handle players using abilities?
The larger and more complex your game, the more you will find yourself using OOP (that’s my experience; I’ve written many modules that were supposed to be a bunch of simple functions, but then come back a few hours later to refactor them into a class).
Yeah, definitely. OOP is perfect for organizing abilities. For example, say you have a lot of projectile abilities such as a fireball. You can create a general, abstract class that represents an ability that spawns a projectile.
Then, create classes for each individual ability, a fireball ability class, for example, that inherit from the abstract projectile spawner class.
This way, when you want to create another ability with the same core mechanic, you won’t end up needing to repeat yourself, and the process of creating it will be simplified as most of the work is already done by the methods of the abstract class (e.g. spawning the projectile, moving it, detecting hits, etc.).
To get a better grasp of just how widely you can apply OOP, look around the community resources page and check out the open source ones. You’ll see a lot of different idioms and structures, but chances are, if you see self in a module, that module uses OOP.
local Projectile= {}
-- Called when constructing the object
-- TouchedCallbacks is a field that all projectiles share by default
-- It stores a table of functions that we call when the projectile hits something
-- This might disable intellisense, however (not sure because I don't rely on intellisense much)
function Projectile.DeclareFields(projectile)
projectile.TouchedCallbacks = {}
end
function Projectile.CreateInstance(projectileName)
-- Clone the model given the name, parent to workspace, etc.
end
-- A possible movement function
function Projectile.ApplyImpulse(projectileModel, vec)
projectileModel.PrimaryPart:ApplyImpulse(vec)
end
-- A second possible movement function
function Projectile.ApplyLinearVelocity(projectileModel, vec)
-- Setup the linear velocity stuff
end
-- Hooks a function that is called when the projectile touches something
-- Of course, you'd want to use something other than Touched, like raycasting
function Projectile.AddTouchedCallback(projectile, callback)
table.insert(projectile.TouchedCallbacks, projectile)
end
-- Detect when the projectile touches stuff so we can invoke callbacks
-- Of course, you'd want to use more reliable queries than the Touched event, like raycasting
function Projectile.EnableTouched(projectile)
if not projectile.Model then
return
end
projectile.TouchedConnection = projectile.Model.BoundingBox.Touched:Connect(function(...)
for i, callback in projectile.TouchedCallbacks do
task.spawn(callback, ...)
end
end)
end
local DEFAULT_PROPERTIES = {
Damage = 30,
BlastRadius = 5,
Velocity = 50,
}
-- Fireball class
local Fireball = {}
Fireball.__index = Fireball
function Fireball.new(properties)
local self = {
Model= Projectile.CreateInstance("Fireball"),
Properties = properties or DEFAULT_PROPERTIES, -- Easily customizable using the properties parameter
}
Projectile.DeclareFields(self) -- Create TouchedCallbacks table
-- Have the fireball explode when it touches something
Projectile.AddTouchedCallback(self, function(...)
self:Explode()
end)
setmetatable(self, Fireball)
return self
end
function Fireball:Launch(directionVector)
Projectile.EnableTouched(self) -- Detect hits
Projectile.ApplyImpulse(self.Model, directionVector * self.Properties.Velocity) -- Move the projectile
end
function Fireball:Explode()
-- Deals damage (self.Properties.Damage) to all humanoids
-- caught in its blast radius (self.Properties.BlastRadius)
end
-- Creating a fireball projectile from an ability (which would be another class!)
-- abilityFireballProperties describes the properties of the fireball this ability wants to create
local fireball = Fireball.new(abilityFireballProperties)
fireball:Launch(lookVector)
Probably not the best code I’ve written (it’s really late for me right now), but I hope you can still get something out of it.
Remember that the example is just how I would structure things. I highly recommend looking into open source projects to see how they do things (probably better than me) and incorporate what you like into your code.