Need drag script to not center when I click on an object

I am trying to make a script that can drag non locked objects in workspace and it works fine except there’s one issue, wherever I click the object it centers it instead of dragging it wherever I clicked it at, I tried fixing this for a while but nothing seemed to work. I am basically trying to make Roblox Studio inside of Roblox Studio so imagine the physics of dragging in Roblox Studio, that’s basically what I’m trying to make. Here is the drag script:

local dragging = false
local draggedPart = nil
local dragOffset = nil
local touchId = nil

local function startDrag(input)
	if flyCameraEnabled and camera.CameraType == Enum.CameraType.Scriptable then
		local target = mouse.Target
		if target and target:IsA("BasePart") and not target.Locked then
			dragging = true
			draggedPart = target
			local mousePos = mouse.Hit.Position
			dragOffset = draggedPart.Position - mousePos
			mouse.TargetFilter = draggedPart
		end
	end
end

local function updateDrag()
	if dragging and draggedPart then
		local mousePos = mouse.Hit.Position
		local newPos = mousePos + dragOffset

		local rayOrigin = camera.CFrame.Position
		local rayDirection = (mouse.Hit.Position - rayOrigin).Unit * 500
		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {draggedPart}
		raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

		local rayResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

		if rayResult then
			local normal = rayResult.Normal
			local hitPos = rayResult.Position
			newPos = hitPos + normal * (draggedPart.Size / 2)
			draggedPart.Position = newPos
		end
	end
end

local function endDrag()
	if dragging and draggedPart then
		ReplicatedStorage:WaitForChild("MovePartRemote"):FireServer(draggedPart, draggedPart.CFrame)
	end
	dragging = false
	if draggedPart then
		mouse.TargetFilter = nil
	end
	draggedPart = nil
	dragOffset = nil
	touchId = nil
end

UserInputService.InputBegan:Connect(function(input, gpe)
	if gpe then return end
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		startDrag(input)
	elseif input.UserInputType == Enum.UserInputType.Touch then
		if flyCameraEnabled then
			local pos = input.Position
			if pos.X > camera.ViewportSize.X * 0.4 then
				touchId = input
				startDrag(input)
			end
		end
	end
end)

UserInputService.InputChanged:Connect(function(input)
	if dragging then
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			updateDrag()
		elseif input.UserInputType == Enum.UserInputType.Touch and input == touchId then
			updateDrag()
		end
	end
end)

UserInputService.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 or (input.UserInputType == Enum.UserInputType.Touch and input == touchId) then
		endDrag()
	end
end)

1 Like

You initialise newPos with dragOffset, but then here, your not even using dragOffset, instead of dividing the part size by 2, try replacing it with drag offset, and you should add instead of multiply if im not mistaken, but I think you get my idea.

1 Like

If I do that it clips through the ground, it does not stay on top anymore

1 Like

I guess try adding onto the (draggedPart.Size / 2), maybe adding the offset from the center might do it?

1 Like

That didn’t work either, I don’t really have an idea how to fix this

1 Like

I think you should store the starting mouseHit and instance position upon starting the drag, and then offset it using a mouseHit delta when actually dragging

Before triggering all of these, when the signal to drag the instance fires, cast another ray ignoring the instance itself so the starter drag position is the hit on the ground.

Also, in you should exclude the instance from getting queried by the mouse hit so they ray always hits the ground, ceiling, or wall, not the instance.