I tried to separate everything into chunks separated by comments… If y’all need me to explain what something does, I absolutely can.
I also left a comment near the end. (line 280)
MainLocalScript.lua (13.4 KB)
Thanks in advance :D
Could you please add script context to post?Some people like me do use devforum on mobile and generally not everyone wants to download anything.
It’s quite a large script, but sure:
-- game:GetService()
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local ContextActionService = game:GetService("ContextActionService")
-- Replicated Storage
local ToolBarKeybindsFolder = ReplicatedStorage:WaitForChild("ToolBarKeybinds")
local PartButtonsKeybindsFolder = ReplicatedStorage:WaitForChild("PartButtonsKeybinds")
local JointButtonsKeybindsFolder = ReplicatedStorage:WaitForChild("JointButtonsKeybinds")
local ConstraintButtonsKeybindsFolder = ReplicatedStorage:WaitForChild("ConstraintButtonsKeybinds")
local SolidModelingButtonsKeybindsFolder = ReplicatedStorage:WaitForChild("SolidModelingButtonsKeybinds")
local AdditionalSettings = ReplicatedStorage:WaitForChild("AdditionalSettings")
local DeselectAllButtonsValue = AdditionalSettings:WaitForChild("DeselectAllButtons")
-- Player and Gui
local Player = game:GetService("Players").LocalPlayer
local camera = workspace.CurrentCamera
local mouse = Player:GetMouse()
local PlayerGui = Player.PlayerGui
local PlayerScripts = Player.PlayerScripts
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local Frame = ScreenGui.Frame
local TopButtons = Frame.TopButtons
local BottomButtons = Frame.BottomButtons
local TopMiddleBar = TopButtons.TopMiddleBar
local TopMiddleKeybindBar = TopButtons.TopMiddleKeybindBar
local BottomMiddleBar = BottomButtons.BottomMiddleBar
local BottomMiddleKeybindBar = BottomButtons.BottomMiddleKeybindBar
local TransformationButtons:Frame = Frame.TransformationButtons
local UseLocalDirectionButton = TransformationButtons.UseLocalDirectionButton
local UseOffsetButton = TransformationButtons.UseOffsetButton
local AdjustOffsetButton = TransformationButtons.AdjustOffsetButton
local PartButtonsPopUp = Frame.PartButtonsPopUp
local PartButtonsBar = PartButtonsPopUp.PartButtonsBar
local PartButtonsKeybindBar = PartButtonsPopUp.PartButtonsKeybindBar
local JointButtonsPopUp = Frame.JointButtonsPopUp
local JointButtonsBar = JointButtonsPopUp.JointButtonsBar
local JointButtonsKeybindBar = JointButtonsPopUp.JointButtonsKeybindBar
local ConstraintButtonsPopUp = Frame.ConstraintButtonsPopUp
local ConstraintButtonsBar = ConstraintButtonsPopUp.ConstraintButtonsBar
local ConstraintButtonsKeybindBar = ConstraintButtonsPopUp.ConstraintButtonsKeybindBar
local SolidModelingButtonsPopUp = Frame.SolidModelingButtonsPopUp
local SolidModelingButtonsBar = SolidModelingButtonsPopUp.SolidModelingButtonsBar
local SolidModelingButtonsKeybindBar = SolidModelingButtonsPopUp.SolidModelingButtonsKeybindBar
--local UnionButton = SolidModelingButtonsBar.Union
--local IntersectButton = SolidModelingButtonsBar.Intersect
--local SubtractButton = SolidModelingButtonsBar.Subtract
--local SeparateButton = SolidModelingButtonsBar.Separate
-- Module Scripts
local CommonlyUsedVariablesModule = require(script.CommonlyUsedVariablesModule)
local ButtonSelectionFunctionsModule = require(script.ButtonSelectionFunctionsModule)
local SelectFunctionsModule = require(script.SelectFunctionsModule)
local MoveFunctionsModule = require(script.MoveFunctionsModule)
local RotateFunctionsModule = require(script.RotateFunctionsModule)
local ResizeFunctionsModule = require(script.ResizeFunctionsModule)
local PartFunctionsModule = require(script.PartFunctionsModule)
local JointFunctionsModule = require(script.JointFunctionsModule)
local ConstraintFunctionsModule = require(script.ConstraintFunctionsModule)
local WeldFunctionsModule = require(script.WeldFunctionsModule)
local SolidModelingFunctionsModule = require(script.SolidModelingFunctionsModule)
--
local debounce = false
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- Setup
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
CommonlyUsedVariablesModule.init()
ButtonSelectionFunctionsModule.init()
PartFunctionsModule.init()
JointFunctionsModule.init()
ConstraintFunctionsModule.init()
-- -- -- -- --
local function createButtonKeybindsArray(ButtonKeybindBar, ButtonKeybindsFolder)
local ButtonsKeybindsArray = {}
for _, button in ButtonKeybindBar:GetChildren() do
if button:IsA("TextButton") then
table.insert(ButtonsKeybindsArray, button)
button.Text = ButtonKeybindsFolder[button.Name].Value
end
end
return ButtonsKeybindsArray
end
local function SelectSpecificPopUpButton(inputKeyCodeName, ButtonPopUp, ButtonKeybindsFolder, FunctionsModule, ButtonsOrderArray)
if ButtonPopUp.Visible == true then
for _, stringValue in ButtonKeybindsFolder:GetChildren() do
if inputKeyCodeName == stringValue.Value then
FunctionsModule.SelectSpecificButton(ButtonsOrderArray[tonumber(stringValue.Name)].Name)
return
end
end
end
end
local function toggle(event, handler)
local conn = nil
local function Toggle()
if conn == nil then
conn = event:Connect(handler)
else
conn:Disconnect()
conn = nil
end
return conn
end
return Toggle
end
-- -- -- -- --
local table1 = createButtonKeybindsArray(TopMiddleKeybindBar, ToolBarKeybindsFolder)
local table2 = createButtonKeybindsArray(BottomMiddleKeybindBar, ToolBarKeybindsFolder)
local ToolBarButtonsKeybindsArray = table.move(table2, 1, #table2, #table1 + 1, table1)
local PartButtonsKeybindsArray = createButtonKeybindsArray(PartButtonsKeybindBar, PartButtonsKeybindsFolder)
local JointButtonsKeybindsArray = createButtonKeybindsArray(JointButtonsKeybindBar, JointButtonsKeybindsFolder)
local ConstraintButtonsKeybindsArray = createButtonKeybindsArray(ConstraintButtonsKeybindBar, ConstraintButtonsKeybindsFolder)
local SolidModelingButtonsKeybindsArray = createButtonKeybindsArray(SolidModelingButtonsKeybindBar, SolidModelingButtonsKeybindsFolder)
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- Toolbar Button Selection
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
for _, button in CommonlyUsedVariablesModule.toolBarButtonsDictionary do -- selects the toolbar buttons when clicked
button.MouseButton1Click:Connect(function()
if debounce == true then
return
end
debounce = true
if button == ButtonSelectionFunctionsModule.currentToolBarButton then
ButtonSelectionFunctionsModule.DeselectCurrentToolBarButton()
else
ButtonSelectionFunctionsModule.DeselectCurrentToolBarButton()
ButtonSelectionFunctionsModule.SelectToolBarButton(button)
end
task.wait(0.1)
debounce = false
return
end)
end
local function listenToKeyPresses(input, gameProcessedEvent)
if gameProcessedEvent == true or debounce == true then
return
end
for _, stringValue in ToolBarKeybindsFolder:GetChildren() do
if stringValue.Value == input.KeyCode.Name then
local button = CommonlyUsedVariablesModule.toolBarButtonsDictionary[stringValue.Name]
if button == ButtonSelectionFunctionsModule.currentToolBarButton then
ButtonSelectionFunctionsModule.DeselectCurrentToolBarButton()
return
end
ButtonSelectionFunctionsModule.DeselectCurrentToolBarButton()
ButtonSelectionFunctionsModule.SelectToolBarButton(button)
return
end
end
SelectSpecificPopUpButton(input.KeyCode.Name, PartButtonsPopUp, PartButtonsKeybindsFolder, PartFunctionsModule, CommonlyUsedVariablesModule.partButtonsOrderArray)
SelectSpecificPopUpButton(input.KeyCode.Name, JointButtonsPopUp, JointButtonsKeybindsFolder, JointFunctionsModule, CommonlyUsedVariablesModule.jointButtonsOrderArray)
SelectSpecificPopUpButton(input.KeyCode.Name, ConstraintButtonsPopUp, ConstraintButtonsKeybindsFolder, ConstraintFunctionsModule, CommonlyUsedVariablesModule.constraintButtonsOrderArray)
SelectSpecificPopUpButton(input.KeyCode.Name, SolidModelingButtonsPopUp, SolidModelingButtonsKeybindsFolder, SolidModelingFunctionsModule, CommonlyUsedVariablesModule.SolidModelingButtonsOrderArray)
if DeselectAllButtonsValue.Value == input.KeyCode.Name then
ButtonSelectionFunctionsModule.DeselectCurrentToolBarButton()
return
end
end
local function changeKeybindWhenClicked(button:TextButton, keybindFolder) -- changes the keybinds
button.MouseButton1Click:Connect(function()
toggle(UserInputService.InputBegan, listenToKeyPresses)
button.Text = "..."
local connection = nil
connection = UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
if gameProcessedEvent == true then
return
end
if input.KeyCode then
button.Text = input.KeyCode.Name
keybindFolder[button.Name].Value = input.KeyCode.Name
toggle(UserInputService.InputBegan, listenToKeyPresses)
connection:Disconnect()
end
end)
end)
end
for _, button in ToolBarButtonsKeybindsArray do
changeKeybindWhenClicked(button, ToolBarKeybindsFolder)
end
for _, button in PartButtonsKeybindsArray do
changeKeybindWhenClicked(button, PartButtonsKeybindsFolder)
end
for _, button in JointButtonsKeybindsArray do
changeKeybindWhenClicked(button, JointButtonsKeybindsFolder)
end
for _, button in ConstraintButtonsKeybindsArray do
changeKeybindWhenClicked(button, ConstraintButtonsKeybindsFolder)
end
for _, button in SolidModelingButtonsKeybindsArray do
changeKeybindWhenClicked(button, SolidModelingButtonsKeybindsFolder)
end
-- -- -- -- --
for button, _ in CommonlyUsedVariablesModule.transformationButtonsSelectedTable do -- selects the transformation button when clicked
button.MouseButton1Click:Connect(function()
if debounce == true then
return
end
debounce = true
ButtonSelectionFunctionsModule.SelectTransformationButton(button)
task.wait(0.1)
debounce = false
return
end)
end
-- also how can this be included?
--[[
local function dragAndSelectFunction(ButtonsBar, FunctionsModule, postion_config, orderArray)
local function dragButtonsToGrid(proposedPosition, proposedRotation)
return UDim2.fromScale(0, math.clamp(math.round(proposedPosition.Y.Scale / postion_config[1]) * postion_config[1], 0, postion_config[2])+postion_config[3]), proposedRotation
end
--
local ButtonsInitialPosition = 0
local ButtonsClick:BoolValue? = nil
local selectedButton:TextButton? = nil
local DragDetectorTable = {}
local ButtonsPositionYScaleTable = {}
for _, button:TextButton in ButtonsBar:GetChildren() do
DragDetectorTable[button] = button:FindFirstChildOfClass("UIDragDetector")
ButtonsPositionYScaleTable[button] = button.Position.Y.Scale
end
-- -- -- -- --
for uiDragDetectorButton:TextButton, uiDragDetector:UIDragDetector in pairs(DragDetectorTable) do
uiDragDetectorButton.MouseEnter:Connect(function()
if selectedButton == nil then
uiDragDetectorButton.BackgroundColor3 = Color3.fromRGB(200, 200, 200)
end
end)
uiDragDetectorButton.MouseLeave:Connect(function()
if selectedButton == nil then
uiDragDetectorButton.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
end
end)
uiDragDetector:AddConstraintFunction(1, dragButtonsToGrid)
uiDragDetector.DragStart:Connect(function(inputPosition)
selectedButton = uiDragDetector.Parent
ButtonsInitialPosition = inputPosition
ButtonsClick = true
end)
uiDragDetector.DragContinue:Connect(function(inputPosition)
if ButtonsClick == true then
if (ButtonsInitialPosition-inputPosition).Magnitude > 10 then
ButtonsClick = false
end
end
for button:TextButton, PositionYScale in ButtonsPositionYScaleTable do
if math.abs(selectedButton.Position.Y.Scale-PositionYScale) < 0.0001 and button ~= selectedButton then
local selectedButtonIndex = table.find(orderArray, selectedButton)
orderArray[ table.find(orderArray, button) ] = selectedButton
orderArray[selectedButtonIndex] = button
button.Position = UDim2.fromScale(1, ButtonsPositionYScaleTable[selectedButton]) -- move the button that got stepped on into the now vacant spot
ButtonsPositionYScaleTable[button] = button.Position.Y.Scale
ButtonsPositionYScaleTable[selectedButton] = selectedButton.Position.Y.Scale
end
end
end)
uiDragDetector.DragEnd:Connect(function(inputPosition)
if ButtonsClick == true then
FunctionsModule.SelectSpecificButton(selectedButton.Name)
end
selectedButton = nil
end)
end
end
-- -- -- -- --
dragAndSelectFunction(
JointButtonsBar,
JointFunctionsModule,
{0.2, 0.8, 0.01}, -- button spacing
CommonlyUsedVariablesModule.jointButtonsOrderArray
)
dragAndSelectFunction(
ConstraintButtonsBar,
ConstraintFunctionsModule,
{0.2, 0.8, 0.01}, -- button spacing
CommonlyUsedVariablesModule.constraintButtonsOrderArray
)
dragAndSelectFunction(
PartButtonsBar,
PartFunctionsModule,
{0.2, 0.8, 0.01}, -- button spacing
CommonlyUsedVariablesModule.partButtonsOrderArray
)
dragAndSelectFunction(
SolidModelingButtonsBar,
SolidModelingFunctionsModule,
{0.25, 1, 0.015}, -- button spacing
CommonlyUsedVariablesModule.SolidModelingButtonsOrderArray
)
]]
To me that pretty readable already so i dunno man, i would probably move requires to be near services and move debounce variable closer to code where it is used.
You should avoid use of anonymous function if possible as since they are kinda costly for perfomance and the fact that you being able to have only 1 function being connected to multiple events instead,avoid any indexing of variables inside table/instances and instead caching the value and lastly you should consider adding typecheck and avoiding all type casting that is possible and using --!strict mode for better linting.
Could you elaborate on the third sentence? What is “anonymous function”? Could you split it up into bullet points and explain them, I’m lost lol
Anonymous functions could be useful if your function always has unique envirement (unique instance/table/user data referances)
In your code i noticed you creating function to every button press event even thought as i see all of them having same exact envirement!
Hower if it does have same envirement that eould be just waste of memory
You could fix it this way:
local function MyButton():()
--Code here
end
For loop
button.MouseButton1Click:Connect(MyButton)
end
:() in the end of function means that it returns an empty tuple (void) aka doesn’t return anything, typecheck thing
Are you talking about this part?
for _, button in ToolBarButtonsKeybindsArray do
changeKeybindWhenClicked(button, ToolBarKeybindsFolder)
end
for _, button in PartButtonsKeybindsArray do
changeKeybindWhenClicked(button, PartButtonsKeybindsFolder)
end
for _, button in JointButtonsKeybindsArray do
changeKeybindWhenClicked(button, JointButtonsKeybindsFolder)
end
for _, button in ConstraintButtonsKeybindsArray do
changeKeybindWhenClicked(button, ConstraintButtonsKeybindsFolder)
end
for _, button in SolidModelingButtonsKeybindsArray do
changeKeybindWhenClicked(button, SolidModelingButtonsKeybindsFolder)
end
No im talking about that…symbol limt
The script itself is already very readable and organized, mainly about the Module Scripts. However, you have a lot of for loops that make similar things. What I mean is this part:
As you can see, there are several similar for loops; the only things that distinguish them are the array and folder. For example:
for _, button in ConstraintButtonsKeybindsArray do
changeKeybindWhenClicked(button, ConstraintButtonsKeybindsFolder)
-- Loop running through array constraint buttons keybinds, and calling function with respective folder
end
for _, button in SolidModelingButtonsKeybindsArray do
changeKeybindWhenClicked(button, SolidModelingButtonsKeybindsFolder)
-- Loop running through array solid modeling button keybinds, and calling function with respective folder.
end
By that, you could store those in a table, called “arrays,” for example. You iterate through this table and then do everything you need:
local arrays = { --> you can make it with string or number indexes, you choose
SolidModelingButtonsKeybinds = {SolidModelingButtonsKeybindsArray, SolidModelingButtonsKeybindsFolder},
ConstraintButtonsKeybinds = {ConstraintButtonsKeybindsArray, ConstraintButtonsKeybindsFolder},
-- The remaining ones you implement above
}
-- Then here you iterate through then:
for _, array in arrays do
local childArray, folder = table.unpack(array)
for _, button in childArray do
changeKeybindWhenClicked(button, folder)
end
end
Way cleaner, isn’t it?
okay, interesting. How would that work?
How would I combine
for button, _ in CommonlyUsedVariablesModule.transformationButtonsSelectedTable do -- selects the transformation button when clicked
button.MouseButton1Click:Connect(function()
if debounce == true then
return
end
debounce = true
ButtonSelectionFunctionsModule.SelectTransformationButton(button)
task.wait(0.1)
debounce = false
return
end)
end
and
for _, button in ToolBarButtonsKeybindsArray do
changeKeybindWhenClicked(button, ToolBarKeybindsFolder)
end
for _, button in PartButtonsKeybindsArray do
changeKeybindWhenClicked(button, PartButtonsKeybindsFolder)
end
for _, button in JointButtonsKeybindsArray do
changeKeybindWhenClicked(button, JointButtonsKeybindsFolder)
end
for _, button in ConstraintButtonsKeybindsArray do
changeKeybindWhenClicked(button, ConstraintButtonsKeybindsFolder)
end
for _, button in SolidModelingButtonsKeybindsArray do
changeKeybindWhenClicked(button, SolidModelingButtonsKeybindsFolder)
end
You could change the anonymous function by creating a named function in your script:
local function clicked(button)
if debounce then return end
debounce = true
ButtonSelectionFunctionsModule.SelectTransformationButton(button)
task.wait(.1)
debounce = false
end
EDIT: Then you replace the anonymous function inside Clicked event and put clicked(button)
.
Person above already pointed about 2nd snippet
local ButtonSelectionFunctionsModule_SelectTransformationButton = ButtonSelectionFunctionsModule.SelectTransformationButton
local button_clicked(button:TextButton):()
if debounce == true then return end
debounce = true
ButtonSelectionFunctionsModule_SelectTransformationButton(button)
task.wait(0.1)
debounce = false
end
end
for button, _ in CommonlyUsedVariablesModule.transformationButtonsSelectedTable do -- selects the transformation button when clicked
button.MouseButton1Click:Connect(function():()
button_clicked(button)
end)
end
Way faster and cleaner,right?
Edit yeah i see a problem its fixed now
Okay I get what you’re saying… basically bring the function from inside the :Connect() to the outside.
Do I do the same thing with the “changeKeybindWhenClicked” function?
But this also makes my code longer… and I have to think of a name for the new function lol
Additionally, I don’t know if it is just me, but I think you should add some spaces accordingly, so the code doesn’t feel “sticky” in lines. If you don’t get what I mean, example:
There are no “break lines” to separate similar parts of the script. Personally, I put “break lines” between if
statements and the rest of the code, or for example, variables, functions… It makes the code more readable. I would format this part like this:
button.MouseButton1Click:Connect(function()
local connection
toggle(UserInputService.InputBegan, listenToKeyPresses)
button.Text = "..."
connection = UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
if gameProcessedEvent then return end
if input.KeyCode then
button.Text = input.KeyCode.Name
keybindFolder[button.Name].Value = input.KeyCode.Name
toggle(UserInputService.InputBegan, listenToKeyPresses)
connection:Disconnect()
end
end)
end)
Don’t worry about perfomance of this btw
Script anslisis will unwrap function to be directly inside connect instesd of function call
Also im not sure hoe you made that function so i cannot say anything for sure
By the way, it is not needed to write if something is existent like that: if x == true then
, because this is the same as if x then
. In my opinion, the second case of checking for the existence of something is more readable, although it doesn’t change anything. It won’t make the code faster or slower.
Yep, I agree, I’ll do that :D
I agree with that too, but I cannot force myself to read them the same way. For some strange reason, there’s a strong feeling of uncertainty when I do if x then
instead of if x == true then
:C
Alright, I’ll try not to