My best practices for roblox dev (mostly on the technical side)

Hey all - I finally took the trouble to publish the notes I’ve made over the years while working with Roblox.
Home some of you find it useful!

https://www.gamedevblog.com/2020/03/best-practices-for-roblox-development.html

42 Likes

Interesting write-up! I do have a few comments.

It’s advisable to use separate RemoteEvent/RemoteFunction objects for every distinct action in your game. If you use 1 RemoteEvent, you need to replicate that extra string that differentiates events for each event that you send. This can add up in bandwidth overhead if you send requests very frequently and the string is a significant portion of the data that you are sending.

These remote messages already need to have an identifier to denote what RemoteEvent they belong to internally, so you may as well just instance separate RemoteEvents and use a little bit of extra memory + initial replication overhead so that you just call a separate RemoteEvent for each action that you need to perform.

I don’t think I can recommend parenting the instance to nil over calling :Destroy() as a general case. It’s easy to introduce reference loops that refer to objects that will keep that instance alive even when you parent it out, depending on how your code is written. Destroy will get rid of all the connections on an object, ensuring that those connections do not keep other objects alive and preventing them from being garbage collected.

For things that need to rotate or move around a lot / for a majority of gameplay, I highly recommend using Motor6D to create joints on your model and then modify those instances instead. Setting the CFrame has a higher cost than manipulating Motor6D offsets, and also Motor6Ds can help you to set nice rotation points so that you don’t need to do extra calculations in your CFrame set calls.

25 Likes

These notes are very useful. I really didn’t know about the Team Create saving, and I thank you for teaching me that!

1 Like

This is a really cool post!

It’d be even better if you went deeper into other things that people can encounter:

  • “Code smells” and how to avoid them. You went into some detail, but why not dedicate a whole section to explaining what they are, etc
  • Incorporating this topic into it. This is an interesting topic (similar to the code smells) that talks about bad habits when it comes to using :FindFirstChild() and :WaitForChild().

Also, one thing I saw in your post was this:

This is true, and there’s also nested-ifs and the “ugliness” that it can cause by taking up more lines. There are times when you can’t have all of the conditions on one line, but you’ll need multiple ifs to make it function properly.

For example (taken from a post about whether it’s okay to use nested-ifs):

if BasePart and BasePart.BrickColor == BrickColor.new("White") then

end

should really be:

if BasePart then
	if BasePart.BrickColor == BrickColor.new("White") then
	end
end

It takes up more lines but if the part doesn’t exist, then it’ll error. Discouraging the use of more if statements might lead to things like that occuring. (although I do agree, pretty code is just as important)

And finally,

If you’ve never used a debugger learn how ffs

The hero we needed, but didn’t deserve. :pray:


How much would they differ in terms of performance or is it just related to bandwidth usage? I’ve always used the same events for similar tasks but if it’s better to use separate ones, then I’ll get into the habit of using those.

1 Like

You are incorrect. and/or operators in Lua will short-circuit. a and b will never evaluate b if a is false/nil, and a or b will never evaluate b if a is truthy.

So the line:

if BasePart and BasePart.BrickColor == BrickColor.new("White") then

is completely valid and will never error, because the right hand side will not be evaluated unless BasePart is not nil.

15 Likes

So even:

if Character and Character.Humanoid then

end

should follow this rule. If the character didn’t exist for some reason, it shouldn’t error, correct?
What would be an example where nested-ifs would be considered better over using things like and, or are nested-ifs always the “worse” solution?

BrickColor is a property so it will always exist if BasePart exists. Character.Humanoid can error because Humanoid is a separate instance and might not have been replicated properly even if Character exists.

Class Change by tktech to change an instance’s class (great for turning one kind of UI item into a different kind)

Personally, I prefer Reclass by Elttob

The most reliable way I’ve found is to set a Model’s CFrame a la model:SetPrimaryPartCFrame( yourTransformHere )

Using that too often leads to your model breaking apart due to floating point errors, if I’m remembering correctly


Other than those and things that others have already mentioned, this is a nice guide! Thanks for sharing your experiences.

5 Likes

You can use guard clauses to avoid edge cases/corner cases. I have a little guide about guard clauses and how to use them.

Other than that, thanks for sharing! I never knew that Team Create can be useful for solo devs like me.

you should do

if Character and Character:FindFirstChildOfClass("Humanoid") then

end

this will never error

and nested-ifs are always the worse solution (for me… after all it is subjective because technically there is no difference)

Just thought I would note It’s better to use FindFirstChildOfClass in this case imo as opposed to relying on the Humanoid’s name being Humanoid

1 Like

It’s pretty significant, plus it’s pretty easy to just write something that creates remotes for you if you don’t want to create every remote you’ll need.

Something like a shared network module where you can do

Network:FireServer("RemoteName",...)
Network:BindEvent("RemoteName",function(...) end)

And it’ll take that remote name, and use the remote or make/wait for one if it doesn’t exist

1 Like

It’s good of you to share this. Everyone should study and learn from other’s experience. Making your own first hand mistakes cost time and money.

Why Reclass and not Class Change? Only a personally advice or something else?

@Thezi, i not controlled, but it preferable to use dot operators (see on developer Hub), so @Polyheximal has right, it not throw a error message, as it was the same as :FindFirstChild() function. The function FindFirstChild was the same as a dot operator, only that it will took more time to load, this is why dot operator is better and not only use the other function.

If you use FindFirstChild function it still can trow a error, so you are wrong.

if typeof(BasePart) == 'Instance' and BasePart:IsA('BasePart') and BasePart.BrickColor == BrickColor.new('White') then

:stuck_out_tongue:

local Character = ...
local Humanoid = Character and Character:FindFirstChildWhichIsA('Humanoid')

if Humanoid then

:stuck_out_tongue:

Sometimes I like to follow this pattern when declaring variables. Nil values will cascade all the way down to the last variable with no error in sight.

Considering the name of the variable, this seems a bit of a pedantic comment and not really advancing the discussion in any way.

I’d recommend you use Luau type annotations over run-time type-checks. You can simply make it so BasePart can only contain a nil or a BasePart.

1 Like

Luau type annotations don’t actually work yet, so nobody can use them seriously. There’s no tool or built-in functionality to strip them out either, so you can’t even only use them for development. Everybody has to wait until production Roblox either supports stripping type annotations out, or full typed functionality.

Of course, playing with types in Studio won’t hurt, especially if you’re maintaining a typed version of your script for once it fully releases, or doing actual beta testing and providing feedback.

My “pedantic” checks work fine and have a minimal performance penalty.

Ok, thanks for commenting that on the side note. The main point above was that for the sake of this particular discussion, it’s fair to assume that the code will only ever set nil or a BasePart in the BasePart variable.

You typically know what range of values your code will be setting in a variable, so you know about your own code when type checking should be necessary or not. Hope that makes sense on why the comment is pedantic. :slightly_smiling_face:

1 Like

Oh, you were referring to the comment, my bad. I thought you were calling my code pedantic.

I would do those kinds of checks on arguments passed to a function, but yeah if I know the exact types that variable could be, I could optimize.

You did say your code could never error, to be fair. “not nil” ~= “an Instance”