Drag Detector Realistic Dragging

I’m working on a realistic dragging system where a player can pick up and drag a part. Currently, if the part gets too far from the player, I use Lerp to pull it back. However, this approach causes buggy behavior—sometimes the part gets pushed even further away instead of smoothly following the player.

My Goal

I want the part to follow the player naturally when dragged, and if it gets too far, it should be pulled back smoothly without jittering or being pushed away. If I could also make the dragging more smooth/realistic that would be great also.

any help/ideas are very appreciated.

Script

local part = script.Parent
local BreakPoint = part:WaitForChild("BreakPoint")
local BreakSound = part:WaitForChild("Break")
local speedThreshold = 12.5

local DragDetector = part:WaitForChild("DragDetector")

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local camera = workspace.CurrentCamera

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local VFXFolder = ReplicatedStorage:WaitForChild("VFX")
local SmallBreakVfx = VFXFolder:WaitForChild("SmallBreak")

local DragLogic = ReplicatedStorage:WaitForChild("DragDetector")
local Debris = game:GetService("Debris")

local debounce = false

local stopRange = 15
local dragRange = 8 

local function onTouched(otherPart)
	local currentSpeed = part.Velocity.Magnitude
	if currentSpeed > speedThreshold and not debounce then
		debounce = true
		print("Break effect")
		BreakSound:Play()
		for _, vfx in ipairs(SmallBreakVfx:GetChildren()) do
			if vfx:IsA("ParticleEmitter") then
				local clone = vfx:Clone()
				clone.Parent = BreakPoint
				clone:Emit(2)
				Debris:AddItem(clone, 2)
			end
		end
		delay(1, function() 
			debounce = false 
		end)
	end
end

part.Touched:Connect(onTouched)

DragDetector.DragStart:Connect(function(player)
	print("Dragging Started")
end)

DragDetector.DragEnd:Connect(function(player)
	print("Dragging Stopped")
end)

DragDetector.DragContinue:Connect(function(player)
	local character = player.Character
	if not character then return end

	local hrp = character:FindFirstChild("HumanoidRootPart")
	if not hrp then return end

	local distance = (part.Position - hrp.Position).Magnitude

	if distance > stopRange then
		print("Drag canceled: part is too far from the player")
		DragDetector.Enabled = false
		task.wait(0.5)
		DragDetector.Enabled = true
		return
	end

	if distance > dragRange then
		local direction = (part.Position - hrp.Position).Unit
		local targetPos = hrp.Position + direction * dragRange
		part.Position = part.Position:Lerp(targetPos, 5)
		print("Part too far: Dragging")
	else
		print("No adjustment needed")
	end
end)


Video Showcase

1 Like

You can use an AlignPosition and attachment combo instead of a DragDetector for a much smoother part dragging effect. You simply have to fire an event to the server to make sure NetworkOwnership gets set to the player holding the object so there’s no stuttering.

Raycast from the camera’s position lookvector, and bind the AlignPosition’s Position property to roughly where the raycast ends. If the raycast comes in contact with a part that’s grabbable, you can parent the AlignPosition’s attachment to the part, or if the grabbed object already has one, simply change the AlignPosition’s Attachment0.

-- [LOCALSCRIPT]
local Mover = storage:FindFirstChild("Grabber") :: AlignPosition

local GrabParams = function() : RaycastParams
	local newParams = RaycastParams.new()
	newParams.FilterType = Enum.RaycastFilterType.Include
	newParams.FilterDescendantsInstances = CollectionSVC:GetTagged("PhysObject")
	return newParams
end

local function GrabCast(camCF : CFrame) : RaycastResult
    return workspace:RayCast(camCF.Position, camCF.LookVector*5, GrabParams())
end

RunSVC.Heartbeat:Connect(function(deltaTime)
    Mover.Position = camera.CFrame.Position + (camera.CFrame.LookVector*5)
end)

local held = nil

mouse.Button1Down:Connect(function()
    local grabbed = GrabCast(camera.CFrame)
    held = (held and not grabbed) and nil or grabbed.Instance
    OwnerChange:FireServer(held, not held and false or true)
    Mover.Attachment0 = (not held) and nil or grabbed and grabbed.Instance:FindFirstChild("GrabAttach")
end)
-- [SERVERSCRIPT]
OwnerChange.OnServerEvent:Connect(function(player : Player, object : Instance, setPlayer : boolean)
    obj:SetNetworkOwner(not setPlayer and nil or player)
end)

I also have a couple of demo clips for an identical system that I’m currently working on:

1 Like

would I be able to delay the part going to the cursor or make it harder to move the part around to simulate the part being heavy?

Yeah, you can simply change the Responsiveness and MaxForce properties of the AlignPosition. The lower each of those are, the less quickly it’ll move objects with more mass.

You can also check to see if the part is too far from the alignposition and break the connection in the Heartbeat connection.

1 Like