Scripting Debate

Hey guys, i’m just gonna keep this post short and simple.

Me and my friend are working together on a project and are debating on the best placement of client and serverscripts.

I like to keep my workspace clean and organised, placing client and server scripts into known locations with folders. All of my modules go into ReplicatedStorage > Modules, all of my server scripts go into ServerScriptService > Server, and my client scripts all go into StarterPlayer > StarterPlayerScripts > Client.

I also dont directly use script.Parent when having GUI. I reference the LocalPlayer.PlayerGui and find the Gui that way. I just find this works better for me, is way more organised.

What he does:

He likes to do the same as me server wise, having one server script inside ServerScriptService > Server, however for his client scripts he places Local Scripts directly into the ScreenGui and Frames. He also references the Gui as script.Parent.

I find this highly unorganised and I end up losing the placement of local scripts doing so, however I know this is how alot of people like to script.

Whats your opinion, which option do you find best suits you?

I would personally side with your friend here - mostly because it’s not a norm and scripters tend to have a personal way of things.

I agree your method looks more organized but at the same time for someone who is used to the second method would find it hard to work around with your organized method given the fact that we tend to place things with a location comfort in mind and we can’t really change it after years of doing it the same way.

No one is right or wrong here, end of the day - it’s the functionality which matters!

6 Likes

Yeah I guess since i’m used to my way of coding it feels more messy to me. I personally just hate the fact of having to go into each screegui/frame to find scripts that can just be placed in StarterPlayer > StarterPlayerScripts

1 Like

There is a reason why one would put a script in the respective UI than in PlayerScripts. ScreenGUI’s by default have the property ResetOnSpawn set to true, this means that a whole new copy of the UI is made when the player’s character is added (on server join or respawn), and the old one is destroyed. On the other hand, a script in PlayerScripts just stays there forever and is not impacted by deaths.

So if you reference to a ScreenGUI that resets on spawn from a player script, you are supposed to update the variable each time the character respawns. Otherwise, when you want to change something to any of the UI’s descendants in the future, said object will have been destroyed.

My conclusion is therefore that scripts that handle UI’s should be parented in the respective ScreenGUI, to account for the ResetOnSpawn behavior.

An example:
You have a simple health count UI, a ScreenGUI which by default has ResetOnSpawn set to true, which contains one TextLabel representing the health.
The script behind its functionality lies in PlayerScripts, and contains the following code:

local plr = game:GetService("Players").LocalPlayer
local ui = plr.PlayerGui.Health
local hpText = ui.TextLabel

while task.wait() do
     hpText.Text = plr.Character.Humanoid.Health
end

Every frame, we refer to an object in the ScreenGUI and change its text property - there are obviously better ways of doing this but for the sake of the example we do it like this.
Suddenly, the player dies. A few seconds pass by, and their character gets re-added; the script throws an error: something along the lines of “attempt to index Text with nil”, this means that our variable hpText is a nil value, the object it references to has been destroyed.

To fix this issue:

local plr = game:GetService("Players").LocalPlayer
local ui = plr.PlayerGui.Health
local hpText = ui.TextLabel

plr.CharacterAdded:Connect(function(_)
     ui = plr.PlayerGui.Health
     hpText = ui.TextLabel
end)

while task.wait() do
     hpText.Text = plr.Character.Humanoid.Health
end

We update the variables to new ones. This obviously is an overcomplicated fix for an issue that is easily prevented by just placing the script within the UI; on player respawn, the script itself is destroyed too and a new one is made.
Edit: you might also need to use WaitForChild or a repeat loop until PlayerGui.Health is found, since the CharacterAdded call happens before the UI’s themselves are made.

Hopefully my explanation isn’t too confusing. If I am saying wrong things, do let me know.

2 Likes

Placing scripts inside other instances, although not technically wrong, is just old practice. I personally don’t do that, and put the code where it’s supposed to be (either in ReplicatedStorage, ServerStorage, or any other script-based container). This is so code isn’t dispersed all over the place, and you can ensure you have efficient organization.

In terms of whether to reference the ScreenGui, then the descendants, I don’t think it matters as much. However, since you’re using a top-down approach where you do not have scripts within guis, referencing the ScreenGui first will definitely make it clearer to readers about what you’re running your code on.

Personally speaking, you should rarely be using these “automatic features”. I have a unique dislike of ResetOnSpawn, simply because it encourages bad code and absolutely no state management. Not only that, if you have extremely large guis and/or a lot of guis that use this property, you’re going to lag every time the character dies.

Your guis should be consistent, and you should be able to know the state of your guis at all times. When the player dies and you need to “reset” the GUI, usually that only means tinkering with a few items. Depending on your implementation, you wouldn’t even need this tinkering at all. Using your health example:

local CharacterHealth = require(someCharacterHealthModule)
--[[
    has a "changed" event that fires whenever the health changes
    if the player dies, the module itself sets the internal value back to 100, and
   the change event is fired again
]]

CharacterHealth.Changed:Connect(function()
    Frame.Size = UDim2.fromScale(CharacterHealth:GetHealth() / 100), 1)
    Health.Text = CharacterHealth:GetHealth()
end)
2 Likes

I agree, this property should definitely not be true by default. It comes down to the project size I’d say, if OP and his colleague are already setting up an entire framework of modules for everything then they should indeed use specific containers. On the other, old-mannered, hand, placing them in their respective UI is the easiest way to avoid confusion. You would then have a problem switching to a more consistent approach, if even due to happen in the future at all.

Your approach prioritizes long-term organization and scalability, while your friend’s method focuses on quick implementation and convenience. If the project is small, their approach might work fine, but for larger, more collaborative projects, your method provides better structure and maintainability.

1 Like

Yeah I never use ResetOnSpawn for any of my Gui’s.