Drag Script not working very well

Hi, I’ve got a drag/carry script which seems to be not working as expected for instance, on tablets/mobiles you can just lift the part but not walk anywhere, on the desktop you can walk and carry but theres not enough force to lift it fully as you need.

Could you help me with making the scripts work please.

--grabrequest in ServerScriptStorage
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local GrabRequest = Instance.new("RemoteEvent")
GrabRequest.Name = "GrabRequest"
GrabRequest.Parent = ReplicatedStorage

local function GrabRequested(player, target)
	local character = player.Character
	if character then
		local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
		if humanoidRootPart then
			local offset = Vector3.new(0, 0, 5)  -- Adjust the offset as needed
			target.CFrame = CFrame.new(humanoidRootPart.Position + offset)
		end
	end
end

GrabRequest.OnServerEvent:Connect(GrabRequested)
-- ServerScriptService > Scripts > Draghandler
--draghandler
-- dependencies:
local replicatedStorage = game:GetService("ReplicatedStorage")
local serverScriptService = game:GetService("ServerScriptService")
local dragController = require(serverScriptService.Modules.DragController)

-- paths:
local network = replicatedStorage.Network
local requestDrag = network.RequestDrag
local dragCancelled = network.DragCancelled


-- main:
function requestDrag.OnServerInvoke(player, part)
	if not dragController.CanDrag(player, part) then return false end
	dragController.Drag(player, part)
	
	return true
end

dragCancelled.OnServerEvent:Connect(function(player)
	-- TODO
end)
-- ServerScriptService > Modules (folder) > DragController (module script)
--dragcontroller script
-- dependencies:
local players = game:GetService("Players")
local collectionService = game:GetService("CollectionService")

-- data:
local playerToDragData = {}


-- DragController:
local module = {}

function module.Drag(player, part)
	assert(typeof(player) == "Instance" and player:IsA("Player"), "bad argument #1 to DragController.Drag, expects Instance<Player>")
	assert(module.CanDrag(player, part), "bad argument #2 to DragController.Drag, expects part that can be dragged")

	part:SetNetworkOwner(player)
end

function module.CanDrag(player, part)
	assert(typeof(player) == "Instance" and player:IsA("Player"), "bad argument #1 to DragController.CanDrag, expects Instance<Player>")

	if module.IsDragging(player) then return false end
	if typeof(part) ~= "Instance" then return false end
	if not part:FindFirstChild("Configuration") then return false end  -- Check if part has a Configuration child
	local owner = part.Owner.Value
	if owner ~= 0 and owner ~= player.UserId then return false end -- TODO: add player whitelist checks

	return true
end

function module.IsDragging(player)
	assert(typeof(player) == "Instance" and player:IsA("Player"), "bad argument #1 to DragController.IsDragging, expects Instance<Player>")
	
	local dragData = playerToDragData[player]
	if dragData == nil then return end
	
	return dragData.IsDragging
end


-- main:
players.PlayerAdded:Connect(function(player)
	playerToDragData[player] = {
		IsDragging = false,
	}
end)

players.PlayerRemoving:Connect(function(player)
	playerToDragData[player] = nil
end)


return module
-- Dragger script in StarterPlayerScripts > Dragger

-- Dependencies:
local replicatedStorage = game:GetService("ReplicatedStorage")
local contextActionService = game:GetService("ContextActionService")
local runService = game:GetService("RunService")
local players = game:GetService("Players")
local userInputService = game:GetService("UserInputService")
local clientModules = replicatedStorage.Modules.Client
local dragController = require(clientModules.DragController)
local inputTypeDetector = require(clientModules.InputTypeDetector)
local localPlayer = players.LocalPlayer
local control
local baseCamera
coroutine.wrap(function()
	local playerModule = localPlayer.PlayerScripts:WaitForChild("PlayerModule")
	control = require(playerModule):GetControls()
	baseCamera = require(playerModule:WaitForChild("CameraModule"):WaitForChild("BaseCamera"))
end)()

-- Paths:
local network = replicatedStorage.Network
local requestDrag = network.RequestDrag
local dragCancelled = network.DragCancelled

-- Data:
local inputTypes = inputTypeDetector.InputTypes
local target
local hit

-- Configuration:
local MAX_GRAB_DISTANCE = 10

-- Main:
contextActionService:BindAction("Drag", function(name, state, input)
	if state == Enum.UserInputState.Begin then
		if dragController.IsDragging then return Enum.ContextActionResult.Pass end
		-- Get head
		local character = localPlayer.Character
		if character == nil then return Enum.ContextActionResult.Pass end
		local head = character:FindFirstChild("Head")
		if head == nil then return Enum.ContextActionResult.Pass end
		-- Get target
		local inputType = inputTypeDetector.GetInputType()
		if inputType == inputTypes.KeyboardAndMouse or inputType == inputTypes.Gamepad then
			if target == nil or hit == nil then return Enum.ContextActionResult.Pass end
		elseif inputType == inputTypes.Touch then
			if input.UserInputType ~= Enum.UserInputType.Touch then return Enum.ContextActionResult.Pass end
			local inputPosition = input.Position
			local screenPointRay = workspace.CurrentCamera:ScreenPointToRay(inputPosition.X, inputPosition.Y)
			local extendedRay = Ray.new(screenPointRay.Origin, screenPointRay.Direction * 1000)
			target, hit = workspace:FindPartOnRay(extendedRay, localPlayer.Character, false, true)
		end
		-- Check if can drag
		if not dragController.CanDrag(target) then return Enum.ContextActionResult.Pass end
		if (head.Position - hit).Magnitude > MAX_GRAB_DISTANCE then return Enum.ContextActionResult.Pass end
		-- Prepare if touch
		if inputType == inputTypes.Touch then
			control:Disable()
		end
		-- Drag
		dragController.Drag(target, hit)
		-- Register drag on server
		local wasCancelled = false
		local connection
		connection = dragController.DragCancelled:Connect(function()
			connection:Disconnect()
			wasCancelled = true
			if inputType ~= inputTypes.Touch then return end
			control:Enable()
		end)
		local isSuccessful = requestDrag:InvokeServer(target)
		if wasCancelled or isSuccessful then return end
		dragController.CancelDrag()
	else
		if state == Enum.UserInputState.End then
			dragController.CancelDrag()
		end
		return Enum.ContextActionResult.Pass
	end
end, false, Enum.UserInputType.MouseButton1, Enum.UserInputType.Touch, Enum.KeyCode.ButtonR2)

while true do
	local inputType = inputTypeDetector.GetInputType()
	if inputType == inputTypes.KeyboardAndMouse or inputType == inputTypes.Gamepad then
		local location = userInputService:GetMouseLocation()
		local viewportPointRay = workspace.CurrentCamera:ViewportPointToRay(location.X, location.Y)
		local extendedRay = Ray.new(viewportPointRay.Origin, viewportPointRay.Direction * 1000)
		target, hit = workspace:FindPartOnRay(extendedRay, localPlayer.Character, false, true)
	end
	runService.Heartbeat:Wait()
end
-- Client-Side Script: ControlHandler

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local player = Players.LocalPlayer

local placementEvent = ReplicatedStorage:WaitForChild("PlacementEvent")

local selectedBlock = nil -- This should be set by the PlacementGuiScript or some similar mechanism

-- Flags
local isMobile = UserInputService.TouchEnabled

-- Movement and Rotation variables
local moveStep = 0.1
local rotateStep = 10

local touchInitialPosition = nil -- To store the initial touch position

-- Dummy function to get the player's plot (replace with your actual function)
local function getPlayersPlot(player)
	return workspace:FindFirstChild(player.Name .. "_Plot")
end

-- Keyboard Controls
local function handleKeyboard(input)
	if not selectedBlock then return end -- Make sure there's a block selected

	if input.KeyCode == Enum.KeyCode.Space then
		placementEvent:FireServer(selectedBlock)
		selectedBlock = nil
	else
		-- The block follows the player's movement
		local playerPosition = player.Character and player.Character:FindFirstChild("HumanoidRootPart") and player.Character.HumanoidRootPart.Position or Vector3.new()
		selectedBlock.Position = playerPosition + Vector3.new(0, 5, 0) -- Offset by 5 studs above the player
	end
end

-- Touch Controls
local function handleTouchTap()
	if selectedBlock then
		placementEvent:FireServer(selectedBlock)
		selectedBlock = nil
	end
end

local function handlePlayerMovement()
	if not selectedBlock then return end -- Make sure there's a block selected

	-- The block follows the player's movement
	local playerPosition = player.Character and player.Character:FindFirstChild("HumanoidRootPart") and player.Character.HumanoidRootPart.Position or Vector3.new()
	selectedBlock.Position = playerPosition + Vector3.new(0, 5, 0) -- Offset by 5 studs above the player
end

-- Main Input Handling
local function handleInput(input, gameProcessed)
	if gameProcessed then
		return
	end

	if isMobile then
		-- Touch controls are handled in their own functions
	else
		handleKeyboard(input)
	end
end

-- Setup
UserInputService.InputBegan:Connect(handleInput)
UserInputService.TouchTap:Connect(handleTouchTap)

-- Make the selected block follow the player
game:GetService("RunService").Heartbeat:Connect(handlePlayerMovement)
-- dependencies:
local userInputService = game:GetService("UserInputService")

-- enums
local inputTypes = {
	KeyboardAndMouse = 1,
	Gamepad = 2,
	Touch = 3,
}

-- data:
local inputTypeToEnumMap = {
	[Enum.UserInputType.MouseButton1] = inputTypes.KeyboardAndMouse,
	[Enum.UserInputType.MouseButton2] = inputTypes.KeyboardAndMouse,
	[Enum.UserInputType.MouseButton3] = inputTypes.KeyboardAndMouse,
	[Enum.UserInputType.MouseWheel] = inputTypes.KeyboardAndMouse,
	[Enum.UserInputType.MouseMovement] = inputTypes.KeyboardAndMouse,
	[Enum.UserInputType.Keyboard] = inputTypes.KeyboardAndMouse,
	[Enum.UserInputType.Touch] = inputTypes.Touch,
	[Enum.UserInputType.Gamepad1] = inputTypes.Gamepad,
	[Enum.UserInputType.Gamepad2] = inputTypes.Gamepad,
	[Enum.UserInputType.Gamepad3] = inputTypes.Gamepad,
	[Enum.UserInputType.Gamepad4] = inputTypes.Gamepad,
	[Enum.UserInputType.Gamepad5] = inputTypes.Gamepad,
	[Enum.UserInputType.Gamepad6] = inputTypes.Gamepad,
	[Enum.UserInputType.Gamepad7] = inputTypes.Gamepad,
	[Enum.UserInputType.Gamepad8] = inputTypes.Gamepad,
}
local inputType = inputTypeToEnumMap[userInputService:GetLastInputType()] or inputTypes.KeyboardAndMouse
local inputTypeChangedBindable = Instance.new("BindableEvent")


-- ClientUtil:
local module = {}

function module.GetInputType()
	return inputType
end


module.InputTypeChanged = inputTypeChangedBindable.Event

module.InputTypes = inputTypes


-- main:
userInputService.LastInputTypeChanged:Connect(function(newInputType)
	local enum = inputTypeToEnumMap[newInputType]
	if enum == nil then return end
	if enum == inputType then return end
	inputType = enum
	inputTypeChangedBindable:Fire(inputType)
end)

return module
-- dependencies:
local runService = game:GetService("RunService")
local collectionService = game:GetService("CollectionService")
local userInputService = game:GetService("UserInputService")
local players = game:GetService("Players")
local localPlayer = players.LocalPlayer
local playerScripts = localPlayer.PlayerScripts
local baseCamera
coroutine.wrap(function()
	baseCamera = require(playerScripts:WaitForChild("PlayerModule"):WaitForChild("CameraModule"):WaitForChild("BaseCamera"))
end)()

-- paths:
local holdTarget = script.HoldTarget
local holdPositionAlignment = script.HoldPositionAlignment
local logic = workspace.Logic

-- data:
local holdGrip = Instance.new("Attachment")
local dragCancelledBindable = Instance.new("BindableEvent")
local draggedBindable = Instance.new("BindableEvent")
local currentHoldGrip
local heldPart

-- configuration:
local DRAG_DISTANCE = 10


-- DragController:
local module = {}

function module.CancelDrag()
	if not module.IsDragging then return end
	currentHoldGrip:Destroy()
	currentHoldGrip = nil
	holdPositionAlignment.Attachment0 = nil
	heldPart = nil
	module.IsDragging = false
	dragCancelledBindable:Fire()
end

function module.Drag(part, position)
	assert(module.CanDrag(part), "bad argument #1 to DragController.Drag, expects part that can be dragged")
	assert(typeof(position) == "Vector3" or position == nil, "bad argument #2 to DragController.Drag, expects Vector3?")
	
	if position == nil then
		position = Vector3.new()
	end
	local cFrame = CFrame.new(position)
	module.IsDragging = true
	heldPart = part
	currentHoldGrip = holdGrip:Clone()
	currentHoldGrip.Position = part.CFrame:ToObjectSpace(cFrame).Position
	currentHoldGrip.Parent = heldPart
	holdPositionAlignment.Attachment0 = currentHoldGrip
	holdTarget.CFrame = cFrame
	draggedBindable:Fire(part, position)
end

function module.CanDrag(part)
	if module.IsDragging then return false end
	if typeof(part) ~= "Instance" then return false end
	if not collectionService:HasTag(part, "Draggable") then return false end
	local owner = part.Owner.Value
	if owner ~= 0 and owner ~= localPlayer.UserId then return false end -- TODO: add player whitelist checks
	
	return true
end

module.IsDragging = false

module.Dragged = draggedBindable.Event

module.DragCancelled = dragCancelledBindable.Event


-- main:
holdGrip.Visible = true
holdPositionAlignment.Parent = logic
holdTarget.Parent = logic

local function merge(a1, a2)
	for _, v2 in ipairs(a2) do
		table.insert(a1, v2)
	end
	
	return a1
end

coroutine.wrap(function()
	while true do
		if module.IsDragging and baseCamera ~= nil then
			-- get necessary data
			local subjectPosition = baseCamera:GetSubjectPosition()
			if subjectPosition == nil then return end
			-- get hit
			local location = userInputService:GetMouseLocation()
			local viewportPointRay = workspace.CurrentCamera:ViewportPointToRay(location.X, location.Y)
			local rayOrigin = viewportPointRay.Origin
			local mousePosition = CFrame.new(rayOrigin, rayOrigin + viewportPointRay.Direction):ToWorldSpace(CFrame.new(0, 0, -1000)).Position
			local targetPosition = CFrame.new(subjectPosition, mousePosition):ToWorldSpace(CFrame.new(0, 0, -DRAG_DISTANCE)).Position
			holdTarget.CFrame = CFrame.new(targetPosition)
		end
		runService.Heartbeat:Wait()
	end
end)()

return module

Roblox supports DragDetectors natively! They do this by using attachments, and an AlignPosition instance tied to Attachment0. It uses a separate attachment to act as the “cursor”, which also acts as Attachment1, and is bound with RunService to constantly update the postilion to the mouse’s hit position in the world.

With this method, all physics are taken into account; networking may have to be set in some circumstances.

I hope this helped - in future I suggest posting less code and more context; output screenshots and short-form snippets of code are desired.

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