Make dragging tool snap parts to surface

heres the snap formula

math.round(x/snap+0.5)*snap

i dont remember much change the / and * with each other

Thanks! This works for snapping it on the ground and preventing it from clipping through. Though, I assume for different orientations I would need to divide the x and z by 2 depending on where I’m dragging it?

This is a bit vague, is “snap” supposed to be the increment at which the part moves, or?

yknow minecraft right? u know how blocks are like snapped to a grid thats just what that line of code does

do that for each x y and z

1 Like

Yeah I get that, but what is the value of “snap” supposed to be?

1 Like

the size of the part aka lets say ur parts size x = 2, y = 1 and z = 4
so for each of them u have to put the snap so

local function snap(Number,Snap)
   return math.round(Number/Snap+ 0.5) * Snap
end
local snappedvector = Vector3.new(snap(--[[urnum]],part.Size.X),snap(--[[urnum]],part.Size.Y),snap(--[[urnum]],part.Size.Z))
1 Like

It would work like this

local part = path.to.part

local mouse = game:GetService("Players").LocalPlayer:GetMouse()

game:GetService("UserInputService").InputChanged:Connect(function(input)
if (input.UserInputType == Enum.UserInputType.MouseMovement) then
local ray = workspace:Raycast(part.Position, Vector3.new(0,10,0))

part.Position = Vector3.new(
math.floor(mouse.Position.X),
part.Size.Y*0.5+ray.Position.Y,
math.floor(mouse.Position.Z)
)
end
end)

Oh, I gotcha. Thanks!

So implementing this, it’s actually mostly what I’m looking for, but I want the part to snap to every 1 stud rather than the part’s size.

For example, the part in this video is snapping to every 4 studs since it’s 4x4x4 in size:

Would you have any idea on how to go about this, or would I just need to mess around with it until it works?

What I have currently:

local x = (math.round(mousePos.X / partBeingDragged.Size.X + 0.5) * partBeingDragged.Size.X)
local y = (math.round(mousePos.Y / partBeingDragged.Size.Y + 0.5) * partBeingDragged.Size.Y)
local z = (math.round(mousePos.Z / partBeingDragged.Size.Z + 0.5) * partBeingDragged.Size.Z)
			
alignPos.Position = Vector3.new(x, y, z)

just round the numbers simply

local x = math.round(mousePos.X)
local y = math.round(mousePos.Y)
local z = math.round(mousePos.Z)
			
alignPos.Position = Vector3.new(x, y, z)
1 Like

Yeah I discovered that a bit after experimenting around a bit lol

Currently, this implementation works best for what I’m doing:

mousePos = Vector3.new(math.floor(mousePos.X), mousePos.Y, math.floor(mousePos.Z))
alignPos.Position = mousePos + Vector3.new(0, partBeingDragged.Size.X / 2, 0)

The only issue with it is that it doesn’t allow snapping against walls you drag the part against, only the floors. I’m not 100% sure if there’s a way to detect if you’re dragging a part against a wall with Roblox’s current features

RayCast allows you to get the Normal Vector of whatever part was hit by the ray. I actually wrote up a solution earlier that utilized the Normal Vector to allowing snapping to floors, ceilings, or walls - but I completely forgot to post it.

local UserInputService = game:GetService ("UserInputService")

local Part = Instance.new ("Part")
Part.Size = Vector3.new (math.random (1, 10), math.random (1, 10), math.random (1, 10))
Part.Anchored = true
Part.Parent = workspace

local Player = game:GetService ("Players").LocalPlayer
local Camera = workspace.CurrentCamera

local Params = RaycastParams.new ()
Params.FilterType = Enum.RaycastFilterType.Blacklist
Params.FilterDescendantsInstances = {Player, Part}

UserInputService.InputChanged:Connect (function (input, gameProcessed)
	if gameProcessed or input.UserInputType ~= Enum.UserInputType.MouseMovement then
		return
		
	end
	
	local mouseLocation = UserInputService:GetMouseLocation ()
	
	local unitRay = Camera:ScreenPointToRay (mouseLocation.X, mouseLocation.Y)
	local result = workspace:Raycast (unitRay.Origin, unitRay.Direction * 500, Params)

	if result and result.Instance then
		local roundedPosition = Vector3.new (math.round (result.Position.X), result.Position.Y, math.round (result.Position.Z))

		Part.Position = roundedPosition + result.Normal * (Part.Size / 2)
	end
end)
2 Likes

you can use TargetSurface to do that. or see what itslevande here said

Wow, thank you! This is exactly what I need!
I completely forgot that Raycast normals existed, that would’ve helped a loooonnngggg time ago… lol

Yeah, I had no idea Roblox natively supported Normal Vectors with Raycasting. I only just found out about them recently and they’ve already helped me solve some problems that I could barely wrap my mind around.

1 Like

Yep. I only found out about them on a previous project I was working on that dealt heavily with Raycasting; very helpful they are.

But nevertheless, thanks for your solution!

1 Like

By chance is this possible to also change on the snap placement such as instead of 1 per stud it can be from 2 and higher?

( Sorry for the post lol. )

With a slight modification, yes. Define SnapDistance to be any number you want it to be, then change the roundedPosition calculation to :

local roundedPosition = Vector3.new (math.round (result.Position.X / SnapDistance) * SnapDistance, 
			result.Position.Y,
			math.round (result.Position.Z / SnapDistance) * SnapDistance)
1 Like

Awesome thank you this is something I had issues aswell.

1 Like

I’m getting nil with this method.

I have an object that follows the mouse, which SHOULD be filtered in the params.
However, it sometimes gets nil, it sometimes gets a result.