I did promise to give some tips, so I will. I trust that you mostly know what youâre doing so Iâll try to keep things short.
The first thing Iâd change is definitely rethink how to do your tagging and parameter work since thatâs relativily easy to fix. I understand that you want to keep things editable in the editor (likely so artists donât have to code to make towers), but thereâs still another user-facing custom variable system that you are ignoring: handles. I say that instead of making the âattributesâ based on instance names, make it based on handles instead. The handle name corresponds to the attribute (âInvertâ, âFullHideâ, SetTransparance", etc.), then you just pick the part that has the information in it. I tested just trying to put all the data in the handle key itself instead of needing to hardcode check for every value, but Luduvo doesnât make handles a real table. Even still, I like handles more because its easier to maintain. You can have a group of âstyle sheetâ instances that you just keep repointing to for every obby part and you wonât need to repeat yourself as much.
The next thing Iâd change is to actually use tags. Much easier to use than the component alias thing I was talking about in the last reply I made. Even though you canât add tags normally through the editor, Iâd just have them attach stub scripts that mark things when it runs. It would literally only do 2 things:
- run
game.Tags.Add("button/pushbox/whathaveyou"),
- Convert all the handles/child parts into attributes and
:Destroy() them. Attributes are much faster to use than using FindFirstChild, and it makes your code MUCH more readable and less hacky. For example, you donât have to have the IName proxy variable because you can safely change the values of attributes without breaking something else down the line. You also donât have to rely on people magically knowing that the material of a button controls whether or not the code knows that its on or not so if someone wants to customize what buttons look like, they donât have to unravel hacky code to do so
Because that brings to another problem I have with this, and thatâs that every button, pushbox, etc. runs its own script. Lower end PC people wanting to play large towers will thank you! Itâs a lot more performant to have 1 script control 100 parts versus 100 scripts controlling 100 parts. Since everything is in tags and attributes now, itâs trivial to just make one (or a small handful) of controller scripts that manage all of your stuff like so:
function Update(_)
for _, button in game.Tags:Find("button") do
-- Button logic
end
for _, pushbox in game.Tags:Find("pushbox") do
-- Pushbox logic
-- Or, you can just put this in a dedicated "pushbox" script. Doesn't matter.
end
end
A neat but kinda useless bonus of using tags is that your stuff can now be dynamically changed at runtime. For example, your pushbox code uses a button too, right? If you code things right, you can basically reuse your button code. Though I do say âuselessâ because it has extremely marginal gains lol.
The final thing Iâd do is add custom signals. The exact moment that multiple scripts need to communicate with each other is the exact moment youâre going to need to use signals. Itâs much nicer to have all the button functionality in a single script, rather than duplicating and distributing activate/deactivate/toggle code across 2-3 different files. For example, Iâd merge a lot of the deactivator script code into the button script like this:
-- PRESS THAT BUTTON.
self:GetSignal("Activate"):Connect(function()
if self.attr.Pressed then return end
self.Material = "Neon"
self.Position += Vector3.new(0,0.5,0)
self.attr.Pressed = true
if self.attr.ColorSensitive then
ActivatePartsByColor()
else
ActivateParts()
end
self:GetSignal("Toggled"):Emit(true)
end)
self:GetSignal("Deactivate"):Connect(function()
if not self.attr.Pressed then return end
self.Position -= Vector3.new(0,0.5,0)
self.Material = "SmoothPlastic"
if self:FindFirstChild("ColorSensitive") then
DeactivatePartsByColor()
DeactivateButtonsByColor()
else
DeactivateParts()
DeactivateButtons()
end
self:GetSignal("Toggled"):Emit(false)
end)
self.Touched:Connect(function(hit: Instance)
if not hit.attr.ButtonInteractible then return end
if not string.find(hit.attr.Type + "|", self.attr.AcceptedTypes) then return end
-- During compilation, you would just combine Accept[Type] into
-- 1 string and separate it with a special character of some sort
if self.attr.Pressed then
self:GetSignal("Activate"):Emit()
else
self:GetSignal("Deactivate"):Emit()
end
end)
And then in the deactivator script, it would simply be this:
Deactivator.Touched:Connect(function(hit: Instance)
if not hit.attr.Type == "player" or not handles.Button.attr.Pressed then return end
print(hit,"deactivated button")
handles.Button:GetSignal("Deactivate"):Emit()
end)
With the handles.Button being the button assigned to that deactivator. If you have a script that needs to extend the functionality of the button, then youâd just listen to a specially designed signal instead:
handles.Button:GetSignal("Toggled"):Connect(function(isPressed) -- useless for this example but still
print(hit.BillboardText," touched pushbox button")
RespawnPushbox()
end)
Again, the button handle has to be assigned in-editor.
The only caveat here would be that if youâre combining this with my earlier suggestion, you obviously canât use self since a single script controls all buttons. Instead, youâd have to do a bit more complicated code:
function Update(_)
for _, button in game.Tags:Find("button") do
if not game.Tags.Has(button, "registered") then
-- Has to be here you don't keep connecting new signals
-- every frame and cause catastrophic memory leaks
button:GetSignal("Activate"):Connect(function()
-- Button Logic
end)
--...
game.Tags.Add(button, "registered")
end
end
for _, pushbox in game.Tags:Find("pushbox") do
if not game.Tags.Has(pushbox, "registered") then
pushbox:GetSignal("Activate"):Connect(function()
-- Pushbox Logic
end)
--...
game.Tags.Add(pushbox, "registered")
end
end
end
Honestly, with these 3 tips alone, I think you can make a pretty good, performant, legible, and scalable library. TBH there are a lot of smaller things Iâd change (like making it so âonâ and âoffâ buttons are 2 user-createable instances that it switches between so that creators can control everything about their buttons stylistically, Activate/Deactivate Parts would not be a different function from Activate/Deactivate Parts by Color, other some small disagreements with how individual settings are handled, etc.) but thatâs not nearly as important or impactful is those 3. Basically nit picks
Sorry for taking so long. Most of this was typed the day of but then I got busy and forgot about it. Hopefully Iâm not too late :p