Mrxtz

Building System

Aug 7th, 2025
248
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.65 KB | Gaming | 0 0
  1. -- Services
  2. local Players = game:GetService("Players")
  3. local ReplicatedStorage = game:GetService("ReplicatedStorage")
  4. local UserInputService = game:GetService("UserInputService")
  5. local RunService = game:GetService("RunService")
  6. local TweenService = game:GetService("TweenService")
  7. local Debris = game:GetService("Debris")
  8.  
  9. -- References to model folders used as tools (Arrows, Balls, Sliders)
  10. local models = ReplicatedStorage:WaitForChild("Models")
  11. local arrowsFolder = models:WaitForChild("Arrows")
  12. local sizeBallsFolder = models:WaitForChild("SizeBalls")
  13. local SlidersFolder = models:WaitForChild("Sliders")
  14.  
  15. -- Player and camera references
  16. local player = Players.LocalPlayer
  17. local camera = workspace.CurrentCamera
  18.  
  19. -- State variables
  20. local currentPart = nil -- The part currently being edited
  21. local isEditing = false -- Whether the player is in edit mode
  22. local mouseHeartbeatConnection -- Connection used for continuously following the mouse
  23.  
  24. -- Tool instances and their drag connections
  25. local arrows = {}
  26. local balls = {}
  27. local sliders = {}
  28.  
  29. local arrowDragConnections = {}
  30. local ballDragConnections = {}
  31. local sliderDragConnections = {}
  32.  
  33. -- Removes existing tools from workspace and disconnects events, except for the currently active tool
  34. local function cleanupOldTools(currentTool)
  35.     if currentTool ~= arrows then
  36.         for i, arrow in pairs(arrows) do
  37.             if arrow and arrow.Parent then
  38.                 arrow:Destroy()
  39.             end
  40.         end
  41.         arrows = {}
  42.         for i, connection in pairs(arrowDragConnections) do
  43.             connection:Disconnect()
  44.         end
  45.         arrowDragConnections = {}
  46.     end
  47.  
  48.     if currentTool ~= balls then
  49.         for i, ball in pairs(balls) do
  50.             if ball and ball.Parent then
  51.                 ball:Destroy()
  52.             end
  53.         end
  54.         balls = {}
  55.         for i, connection in pairs(ballDragConnections) do
  56.             connection:Disconnect()
  57.         end
  58.         ballDragConnections = {}
  59.     end
  60.  
  61.     if currentTool ~= sliders then
  62.         for i, slider in pairs(sliders) do
  63.             if slider and slider.Parent then
  64.                 slider:Destroy()
  65.             end
  66.         end
  67.         sliders = {}
  68.         for i, connection in pairs(sliderDragConnections) do
  69.             connection:Disconnect()
  70.         end
  71.         sliderDragConnections = {}
  72.     end
  73. end
  74.  
  75. -- Makes the currentPart follow the mouse by raycasting from the screen to the world
  76. local function followMouse()
  77.     cleanupOldTools()
  78.  
  79.     if mouseHeartbeatConnection then
  80.         mouseHeartbeatConnection:Disconnect()
  81.     end
  82.  
  83.     mouseHeartbeatConnection = RunService.Heartbeat:Connect(function()
  84.         if not isEditing or not currentPart then return end
  85.  
  86.         local mousePos = UserInputService:GetMouseLocation()
  87.         local unitRay = camera:ViewportPointToRay(mousePos.X, mousePos.Y)
  88.  
  89.         local raycastParams = RaycastParams.new()
  90.         raycastParams.FilterType = Enum.RaycastFilterType.Exclude
  91.         raycastParams.FilterDescendantsInstances = {currentPart}
  92.  
  93.         local result = workspace:Raycast(unitRay.Origin, unitRay.Direction * 10000, raycastParams)
  94.         if result then
  95.             local surfaceNormal = result.Normal
  96.             local partSize = currentPart.Size
  97.             local offsetDistance = math.max(
  98.                 math.abs(surfaceNormal.X) * partSize.X,
  99.                 math.abs(surfaceNormal.Y) * partSize.Y,
  100.                 math.abs(surfaceNormal.Z) * partSize.Z
  101.             ) / 2
  102.  
  103.             -- Offset to keep the part slightly above the surface
  104.             currentPart.Position = result.Position + (surfaceNormal * offsetDistance)
  105.         end
  106.     end)
  107. end
  108.  
  109. -- Handle input from keyboard and mouse
  110. UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessedEvent: boolean)
  111.     if gameProcessedEvent then return end
  112.  
  113.     -- Press F to enter editing mode and create a transparent part
  114.     if input.KeyCode == Enum.KeyCode.F then
  115.         isEditing = true
  116.  
  117.         local part = Instance.new("Part")
  118.         part.Parent = workspace
  119.         part.Anchored = true
  120.         part.Transparency = 0.5
  121.         part.CanCollide = false
  122.         currentPart = part
  123.  
  124.         if mouseHeartbeatConnection then
  125.             mouseHeartbeatConnection:Disconnect()
  126.         end
  127.  
  128.         -- Make the part follow the mouse cursor
  129.         mouseHeartbeatConnection = RunService.Heartbeat:Connect(function()
  130.             if not isEditing or not currentPart then return end
  131.  
  132.             local mousePos = UserInputService:GetMouseLocation()
  133.             local unitRay = camera:ViewportPointToRay(mousePos.X, mousePos.Y)
  134.  
  135.             local raycastParams = RaycastParams.new()
  136.             raycastParams.FilterType = Enum.RaycastFilterType.Exclude
  137.             raycastParams.FilterDescendantsInstances = {part}
  138.  
  139.             local result = workspace:Raycast(unitRay.Origin, unitRay.Direction * 10000, raycastParams)
  140.             if result then
  141.                 local surfaceNormal = result.Normal
  142.                 local partSize = currentPart.Size
  143.                 local offsetDistance = math.max(
  144.                     math.abs(surfaceNormal.X) * partSize.X,
  145.                     math.abs(surfaceNormal.Y) * partSize.Y,
  146.                     math.abs(surfaceNormal.Z) * partSize.Z
  147.                 ) / 2
  148.  
  149.                 currentPart.Position = result.Position + (surfaceNormal * offsetDistance)
  150.             end
  151.         end)
  152.     end
  153.  
  154.     -- Press 2 to spawn arrows for moving the part
  155.     if input.KeyCode == Enum.KeyCode.Two then
  156.         if not isEditing or not currentPart then return end
  157.         cleanupOldTools(arrows)
  158.  
  159.         if mouseHeartbeatConnection then
  160.             mouseHeartbeatConnection:Disconnect()
  161.         end
  162.  
  163.         -- Clone arrow models from ReplicatedStorage
  164.         for i, v in pairs(arrowsFolder:GetChildren()) do
  165.             local arrowClone = v:Clone()
  166.             arrowClone.Parent = workspace
  167.             table.insert(arrows, arrowClone)
  168.         end
  169.  
  170.         -- Position arrows around the part and connect drag events
  171.         for i, arrow in pairs(arrows) do
  172.             local arrowAxis = arrow:GetAttribute("Axis")
  173.             local axisGap = Vector3.new(0, 0, 0)
  174.  
  175.             if arrowAxis == "X" then axisGap = Vector3.new(-4, 0, 0)
  176.             elseif arrowAxis == "Y" then axisGap = Vector3.new(0, 10, 0)
  177.             elseif arrowAxis == "Z" then axisGap = Vector3.new(0, 0, 7)
  178.             end
  179.  
  180.             local offset = (currentPart.Size / 2) * axisGap
  181.             arrow.Position = currentPart.Position + offset
  182.  
  183.             -- Setup drag behavior
  184.             local dragDetector = arrow:FindFirstChild("DragDetector")
  185.             if dragDetector then
  186.                 local dragStartConnection = dragDetector.DragStart:Connect(function()
  187.                     arrow:SetAttribute("InitialOffset", arrow.Position - currentPart.Position)
  188.                 end)
  189.  
  190.                 local dragConnection = dragDetector.DragContinue:Connect(function()
  191.                     if currentPart then
  192.                         local initialOffset = arrow:GetAttribute("InitialOffset")
  193.                         if initialOffset then
  194.                             currentPart.Position = arrow.Position - initialOffset
  195.  
  196.                             -- Update other arrows to follow the new position
  197.                             for j, otherArrow in pairs(arrows) do
  198.                                 if otherArrow ~= arrow and otherArrow.Parent then
  199.                                     local otherAxis = otherArrow:GetAttribute("Axis")
  200.                                     local otherAxisGap = Vector3.new(0, 0, 0)
  201.                                     if otherAxis == "X" then otherAxisGap = Vector3.new(-4, 0, 0)
  202.                                     elseif otherAxis == "Y" then otherAxisGap = Vector3.new(0, 10, 0)
  203.                                     elseif otherAxis == "Z" then otherAxisGap = Vector3.new(0, 0, 7)
  204.                                     end
  205.                                     local otherOffset = (currentPart.Size / 2) * otherAxisGap
  206.                                     otherArrow.Position = currentPart.Position + otherOffset
  207.                                 end
  208.                             end
  209.                         end
  210.                     end
  211.                 end)
  212.  
  213.                 table.insert(arrowDragConnections, dragStartConnection)
  214.                 table.insert(arrowDragConnections, dragConnection)
  215.             end
  216.         end
  217.     end
  218.  
  219.     -- Press 1 to make the part follow the mouse again
  220.     if input.KeyCode == Enum.KeyCode.One then
  221.         if not isEditing or not currentPart then return end
  222.         followMouse()
  223.     end
  224.  
  225.     -- Press 3 to spawn balls to resize the part
  226.     if input.KeyCode == Enum.KeyCode.Three then
  227.         if not isEditing or not currentPart then return end
  228.         cleanupOldTools(balls)
  229.  
  230.         if mouseHeartbeatConnection then
  231.             mouseHeartbeatConnection:Disconnect()
  232.         end
  233.  
  234.         -- Clone and position resizing balls
  235.         for i, v in pairs(sizeBallsFolder:GetChildren()) do
  236.             local ballClone = v:Clone()
  237.             ballClone.Parent = workspace
  238.             table.insert(balls, ballClone)
  239.         end
  240.  
  241.         for i, ball in pairs(balls) do
  242.             local ballAxis = ball:GetAttribute("Axis")
  243.             local axisGap = Vector3.new(0, 0, 0)
  244.  
  245.             if ballAxis == "X" then axisGap = Vector3.new(-2, 0, 0)
  246.             elseif ballAxis == "Y" then axisGap = Vector3.new(0, 5, 0)
  247.             elseif ballAxis == "Z" then axisGap = Vector3.new(0, 0, 3.5)
  248.             end
  249.  
  250.             local offset = (currentPart.Size / 3) * axisGap
  251.             ball.Position = currentPart.Position + offset
  252.  
  253.             local dragDetector = ball:FindFirstChild("DragDetector")
  254.             if dragDetector then
  255.                 local dragStartConnection = dragDetector.DragStart:Connect(function()
  256.                     ball:SetAttribute("InitialBallPosition", ball.Position)
  257.                     ball:SetAttribute("InitialPartSize", currentPart.Size)
  258.                     ball:SetAttribute("InitialPartPosition", currentPart.Position)
  259.                 end)
  260.  
  261.                 local dragConnection = dragDetector.DragContinue:Connect(function()
  262.                     if currentPart then
  263.                         local initialBallPos = ball:GetAttribute("InitialBallPosition")
  264.                         local initialPartSize = ball:GetAttribute("InitialPartSize")
  265.                         local initialPartPos = ball:GetAttribute("InitialPartPosition")
  266.  
  267.                         if initialBallPos and initialPartSize and initialPartPos then
  268.                             local deltaPos = ball.Position - initialBallPos
  269.                             local newSize = initialPartSize
  270.  
  271.                             if ballAxis == "X" then
  272.                                 local delta = math.abs(deltaPos.X) * 2
  273.                                 if deltaPos.X > 0 then delta = -delta end
  274.                                 newSize = Vector3.new(math.max(0.1, initialPartSize.X + delta), initialPartSize.Y, initialPartSize.Z)
  275.                             elseif ballAxis == "Y" then
  276.                                 local delta = math.abs(deltaPos.Y) * 2
  277.                                 if deltaPos.Y < 0 then delta = -delta end
  278.                                 newSize = Vector3.new(initialPartSize.X, math.max(0.1, initialPartSize.Y + delta), initialPartSize.Z)
  279.                             elseif ballAxis == "Z" then
  280.                                 local delta = math.abs(deltaPos.Z) * 2
  281.                                 if deltaPos.Z < 0 then delta = -delta end
  282.                                 newSize = Vector3.new(initialPartSize.X, initialPartSize.Y, math.max(0.1, initialPartSize.Z + delta))
  283.                             end
  284.  
  285.                             currentPart.Size = newSize
  286.                         end
  287.                     end
  288.                 end)
  289.  
  290.                 table.insert(ballDragConnections, dragStartConnection)
  291.                 table.insert(ballDragConnections, dragConnection)
  292.             end
  293.         end
  294.     end
  295.  
  296.     -- Press 4 to spawn sliders to rotate the part
  297.     if input.KeyCode == Enum.KeyCode.Four then
  298.         if not isEditing or not currentPart then return end
  299.         cleanupOldTools(sliders)
  300.  
  301.         if mouseHeartbeatConnection then
  302.             mouseHeartbeatConnection:Disconnect()
  303.         end
  304.  
  305.         for i, v in pairs(SlidersFolder:GetChildren()) do
  306.             local circleClone = v:Clone()
  307.             circleClone.Parent = workspace
  308.             table.insert(sliders, circleClone)
  309.         end
  310.  
  311.         for i, slider in pairs(sliders) do
  312.             local axis = slider:GetAttribute("Axis")
  313.             local axisGap = Vector3.new(0, 0, 0)
  314.  
  315.             if axis == "X" then axisGap = Vector3.new(-2, 0, -4)
  316.             elseif axis == "Y" then axisGap = Vector3.new(1, 0, 3)
  317.             elseif axis == "Z" then axisGap = Vector3.new(-2, 8, 2.5)
  318.             end
  319.  
  320.             local offset = (currentPart.Size / 3) * axisGap
  321.             slider.Position = currentPart.Position + offset
  322.  
  323.             local dragDetector = slider:FindFirstChild("DragDetector")
  324.             if dragDetector then
  325.                 local dragStartConnection = dragDetector.DragStart:Connect(function()
  326.                     slider:SetAttribute("InitialPartRotation", currentPart.CFrame)
  327.                     slider:SetAttribute("InitialSliderPosition", slider.Position)
  328.                     slider:SetAttribute("LastMousePosition", UserInputService:GetMouseLocation())
  329.                 end)
  330.  
  331.                 local dragConnection = dragDetector.DragContinue:Connect(function()
  332.                     if currentPart then
  333.                         local initialRotation = slider:GetAttribute("InitialPartRotation")
  334.                         local lastMousePos = slider:GetAttribute("LastMousePosition")
  335.  
  336.                         if initialRotation and lastMousePos then
  337.                             local currentMousePos = UserInputService:GetMouseLocation()
  338.                             local mouseDelta = currentMousePos - lastMousePos
  339.  
  340.                             local rotationSensitivity = 0.01
  341.                             local rotationAmount = 0
  342.  
  343.                             if axis == "X" then
  344.                                 rotationAmount = -mouseDelta.Y * rotationSensitivity
  345.                                 currentPart.CFrame = initialRotation * CFrame.Angles(rotationAmount, 0, 0)
  346.                             elseif axis == "Y" then
  347.                                 rotationAmount = mouseDelta.X * rotationSensitivity
  348.                                 currentPart.CFrame = initialRotation * CFrame.Angles(0, rotationAmount, 0)
  349.                             elseif axis == "Z" then
  350.                                 rotationAmount = (mouseDelta.X + mouseDelta.Y) * rotationSensitivity * 0.5
  351.                                 currentPart.CFrame = initialRotation * CFrame.Angles(0, 0, rotationAmount)
  352.                             end
  353.                         end
  354.                     end
  355.                 end)
  356.  
  357.                 table.insert(sliderDragConnections, dragStartConnection)
  358.                 table.insert(sliderDragConnections, dragConnection)
  359.             end
  360.         end
  361.     end
  362.  
  363.     -- Left mouse button to finalize and place the part
  364.     if input.UserInputType == Enum.UserInputType.MouseButton1 then
  365.         if currentPart then
  366.             isEditing = false
  367.             currentPart.Transparency = 0
  368.             currentPart.CanCollide = true
  369.  
  370.             cleanupOldTools()
  371.  
  372.             if mouseHeartbeatConnection then
  373.                 mouseHeartbeatConnection:Disconnect()
  374.             end
  375.             currentPart = nil
  376.         end
  377.     end
  378. end)
  379.  
Advertisement
Add Comment
Please, Sign In to add comment