How to code a drag detector(CLICK TO DRAG SYSTEM)

Hello! me and my friend have recently been trying to make a farming game, and the biggest problem we have encountered so far was our dragging system. essentially, were trying to code a drag detector, but rather than physically needing to drag it, it was a toggle(if you clicked, the part would continue following your first person cursor until you clicked again)

the main issue is latency. we have tried to rotate the object around the player client side, but it wont update on the server, and dropping items has a small delay before they actually drop(when we try changing the CFrame of the part when we drop, as the remote event has to communicate with the server; dependant on ping). if we try lerping the position on both the client and the server, then the lerping becomes really choppy. if we do the lerping on just the server, not only is it really choppy, but people with high ping are going to have a bad time.

currently this is what we have done(its like our third iteration)
client side


server side

as you can see, client sided i am holding an item, but on the server, the object is still in its original position.
if anyone can give any suggestions it would be greatly appreciated

Is the script doing all of that a local script?

its a server and local, heres the code
LOCAL ~

local uip = game:GetService("UserInputService")

local holdCropRE = game.ReplicatedStorage:WaitForChild("HoldCrop")
local releaseCropRE = game.ReplicatedStorage:WaitForChild("ReleaseCrop")

local plr = game.Players.LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local hum = char:FindFirstChild("Humanoid")
 -- wait come down to the re
local camera = workspace.CurrentCamera
plr.CameraMode = Enum.CameraMode.LockFirstPerson

local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {char}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude

local highlight = Instance.new("Highlight")
highlight.FillTransparency = 1
highlight.DepthMode = Enum.HighlightDepthMode.Occluded

uip.MouseIcon = "http://www.roblox.com/asset/?id=13580635690"

local objectBeingSelected --thing raycast hits
local mostRecentlyHeldObject --last held object
local isHoldingItem = false --are you holding something out right now?

game:GetService("RunService").Heartbeat:Connect(function()
	objectBeingSelected = workspace:Raycast(camera.CFrame.Position, camera.CFrame.LookVector * 10, raycastParams)

	-- move the crop both server side and locally
	if isHoldingItem and mostRecentlyHeldObject then
		local offset = camera.CFrame.LookVector * 5
		mostRecentlyHeldObject.CFrame = mostRecentlyHeldObject.CFrame:Lerp(CFrame.new(camera.CFrame.Position + offset), 0.3)

		holdCropRE:FireServer(camera.CFrame, mostRecentlyHeldObject)
	end

	-- determine which object to highlight
	local objectToHighlight = nil
	if isHoldingItem and mostRecentlyHeldObject then
		objectToHighlight = mostRecentlyHeldObject
	elseif objectBeingSelected and objectBeingSelected.Instance and objectBeingSelected.Instance:HasTag("Crop") then
		objectToHighlight = objectBeingSelected.Instance
	end

	-- apply/clear highlight
	if objectToHighlight then
		highlight.Parent = objectToHighlight
		highlight.Adornee = objectToHighlight
	else
		highlight.Parent = nil
		highlight.Adornee = nil
	end

	if uip:IsKeyDown(Enum.KeyCode.V) then
		uip.MouseBehavior = Enum.MouseBehavior.Default
	end
end)

-- server sends all clients the position of the held item, and its owner
holdCropRE.OnClientEvent:Connect(function(ownedBy: Player, objectCFrame: CFrame, selectedObject: BasePart)
	if ownedBy == plr then -- if i am the owner, ignore the server's request
		print("owned by " .. tostring(ownedBy) .. " (me)")
	else -- if i am not the owner, heed the server's request
		print("owned by " .. tostring(ownedBy))
		selectedObject.CFrame = objectCFrame
	end
end)

uip.InputBegan:Connect(function(input: InputObject, gpe: boolean)
	if gpe then return end
	--if you click, drop/release object 
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		if isHoldingItem then
			-- DROPPING OBJECT
			isHoldingItem = false
			if mostRecentlyHeldObject then
				mostRecentlyHeldObject.Anchored = false
				mostRecentlyHeldObject.CanTouch = true
				mostRecentlyHeldObject.AssemblyLinearVelocity = Vector3.new(0,0,0)
				
				releaseCropRE:FireServer(mostRecentlyHeldObject, mostRecentlyHeldObject.CFrame)
				mostRecentlyHeldObject = nil

			end
		elseif objectBeingSelected and objectBeingSelected.Instance and objectBeingSelected.Instance:HasTag("Crop") then
			--PICKING UP OBJECT
			isHoldingItem = true
			mostRecentlyHeldObject = objectBeingSelected.Instance
			
			mostRecentlyHeldObject.Anchored = true
			mostRecentlyHeldObject.CanTouch = false
			mostRecentlyHeldObject.CanCollide = true
			if mostRecentlyHeldObject:GetAttribute("IsWelded") then
				for i, v in mostRecentlyHeldObject.Parent:GetDescendants() do --assuming its welded to a model/union
					if v:IsA("Weld") and v.Part1 == mostRecentlyHeldObject then
						v:Destroy()
						mostRecentlyHeldObject.Parent = workspace
					end
				end
			end
		end
	end
end)

releaseCropRE.OnClientEvent:Connect(function(recentlyOwnedBy: Player, recentlySelectedObject: BasePart, recentObjectCFrame: CFrame)
	if recentlyOwnedBy == plr then
		print("owned by " .. tostring(recentlyOwnedBy) .. " (me)")
	else
		print("owned by " .. tostring(recentlyOwnedBy))
		recentlySelectedObject.CFrame = recentObjectCFrame
		recentlySelectedObject.Anchored = false
		recentlySelectedObject.CanTouch = true
		recentlySelectedObject.AssemblyLinearVelocity = Vector3.new(0,0,0)
	end
end)

SERVER ~

local holdCrop = game.ReplicatedStorage:WaitForChild("HoldCrop")
local releaseCrop = game.ReplicatedStorage:WaitForChild("ReleaseCrop")

holdCrop.OnServerEvent:Connect(function(player: Player, cameraCFrame: CFrame, selectedObject: BasePart)
	local offset = cameraCFrame.LookVector * 5
	--selectedObject.CFrame = selectedObject.CFrame:Lerp(CFrame.new(cameraCFrame.Position + offset), 0.3)
	--print(CFrame.new(cameraCFrame.Position + offset).Position)
	selectedObject.Anchored = true
	selectedObject.CanTouch = false
	selectedObject.CanCollide = true
	if selectedObject:GetAttribute("IsWelded") then
		for i, v in selectedObject.Parent:GetDescendants() do --assuming its welded to a model/union
			if v:IsA("Weld") and v.Part1 == selectedObject then
				v:Destroy()
				selectedObject.Parent = workspace
			end
		end
	end
	holdCrop:FireAllClients(player, CFrame.new(cameraCFrame.Position + offset), selectedObject)
end)

releaseCrop.OnServerEvent:Connect(function(player, selectedObject:BasePart, objectCFrame: CFrame)
	selectedObject:SetAttribute("OwnedBy", player.Name)
	selectedObject.Anchored = false
	selectedObject.CFrame = objectCFrame
	selectedObject.CanTouch = true
	--selectedObject.AssemblyLinearVelocity = Vector3.new(0,0,0)
	-- this is done client side now

	releaseCrop:FireAllClients(player, selectedObject, objectCFrame)
end)

game.Players.PlayerAdded:Connect(function(plr)
	local char = plr.Character or plr.CharacterAdded:Wait()
	for _, v in char:GetDescendants() do
		if v:IsA("BasePart") then
			v.CollisionGroup = "Player"
		end
	end
end)

yeah its very messy

Make sure to set the NetworkOwnership of the part to the player (on server), part:SetNetworkOwner(plr) this’ll allow changes like sliding the object on the client to be replicated on the server.

You’ll also need to set it back to nil part:SetNetworkOwner(nil) or back to auto part:SetNetworkOwnershipAuto() when you the player releases the object. Which one do you choose? Well I’ll choose auto since it’s less laggy however exploiters can take advantage of of stuff set to auto, or really anything they have networkownership of, honestly, shouldn’t worry about it too much.

Also why not use the built in DragDetectors by Roblox?

1 Like

Yeah, I don’t really know how to deal with this – I am not an experienced scripter, but still can script. Sorry, just let the other guy handle this with you.