Having issues with adding snapping to my building system

I have a building system that I am currently trying to add snapping to. This is what the script currently does:


I have tried multiple ways to move the preview object to the snapping point but each method either does nothing or does this ^^^

This is the script that is having troubles (it’s a local script inside of a tool):

local PlacementEvent = game.ReplicatedStorage.PlacementEvent
local ObjectFolder = game.ReplicatedStorage:WaitForChild("BuildingObjectFolder")

local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Mouse = Player:GetMouse()

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local PlacingObject = false
local RotatingObject = false
local functionRunning = false

local PreviewObject

script.Parent.Activated:Connect(function()
	if PlacingObject == false then
		functionRunning = true
		PlacingObject = true
		local RotationAmount = 0
		PreviewObject = ObjectFolder:FindFirstChild(script.Parent.Name):Clone()
		PreviewObject.Parent = game.Workspace

		for i, Parts in pairs(PreviewObject:GetDescendants()) do
			if Parts:IsA("BasePart") then
				Parts.Transparency = .5
				Parts.CanCollide = false
			end
		end
		UserInputService.InputBegan:Connect(function(Key, GameProcessed)
			if not GameProcessed then
				if Key.KeyCode == Enum.KeyCode.R then
					RotatingObject = true
					while RotatingObject == true do 
						wait()
						RotationAmount += 2								
					end							
				end						
			end					
		end)
		UserInputService.InputEnded:Connect(function(Key)
			if Key.KeyCode == Enum.KeyCode.R then
				RotatingObject = false
			end
		end)
		RunService.RenderStepped:Connect(function()
			if Mouse.Target == nil then return end
			if PlacingObject == true then
				Mouse.TargetFilter = PreviewObject
				local prime = PreviewObject:FindFirstChild("BuildingPrimary")
				if prime then
					if Mouse.Target.Name == "BuildingPrimary" then
						if script.Parent.BuildingType.Value == "Foundation" then
							local attachmentList = {}
							for i, v in pairs(prime:GetChildren()) do
								if v.Name == "FoundationAttachment" then
									table.insert(attachmentList, v)
								end
							end

							local closestAttachment = nil
							for _,attachment in attachmentList do
								if closestAttachment == nil then
									closestAttachment = attachment
								else
									if (Mouse.Hit.Position - closestAttachment.WorldPosition).Magnitude > (Mouse.Hit.Position - attachment.WorldPosition).Magnitude then
										closestAttachment = attachment
									end
								end
							end
							local Cframe = prime.CFrame * CFrame.new(closestAttachment.Position)
							PreviewObject:SetPrimaryPartCFrame(Cframe)
						end
					else
						local ObjectCFrame = CFrame.new(Mouse.Hit.Position.X, Mouse.Hit.Position.Y + PreviewObject.PrimaryPart.Size.Y/2, Mouse.Hit.Position.Z)
						local ObjectAngles = CFrame.Angles(0, math.rad(RotationAmount), 0)
						PreviewObject:SetPrimaryPartCFrame(ObjectCFrame * ObjectAngles)
					end
				end
			end
		end)
		UserInputService.InputBegan:Connect(function(Key, GameProcessed)
			if not GameProcessed then
				if Key.KeyCode == Enum.KeyCode.F then
					if PlacingObject == true and functionRunning == true then
						PlacementEvent:FireServer(PreviewObject.Name, PreviewObject.PrimaryPart.CFrame, script.Parent.Name)
						PreviewObject:Destroy()
						PlacingObject = false
						functionRunning = false
						if Player.PlayerGui:FindFirstChild("BuildUI") then
							Player.PlayerGui:FindFirstChild("BuildUI"):Destroy()
						end
						return
					end
				end
			end
		end)
	end
end)
1 Like

Never made a building system but my guess is that you would need to create a grid with nodes.

Once you create that, you’ll use RunService to constantly measure the closest node to your current mouse, and center the part onto that node.

That should give you the snappiness you are looking for.

If you get performance issues from the calculations, which is incredibly unlikely, you could use Octrees to only render the relevant grid positions.

Cheers!

1 Like

To snap a position to a grid, you can use this formula:

local snappedPosition = math.round(originalPosition / gridSize) * gridSize
1 Like

I just figured it out! It’s because I accidently was setting the position to the attachments of the preview object itself, not the mouse target!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.