hi, i’m trying to make a dragging system, similar to the one below:
this system is exactly what i need, and i can come pretty close to that with a simple drag detector:
however, while the x and y axis work fine, if you try moving in the z axis relative to the player, this happens:
i’ve noticed that in the game that i showed that has proper dragging, the distance between the object and the camera is always the same, so i’m pretty sure its moved through camera position, ect., but i’ve been trying to get this to work for atleast 5 hours now and i haven’t even come close to making it work with the z axis.
Without looking at the script directly, you most likely need to add an offset based on the players position and update it using RunService.RenderStepped:Connect
You can take a look at this script that I put together to get some ideas, though mine uses mouse position instead of camera position to update the parts position directly. Also, this is local side, so it does not effect other players.
Hope it helps
Local script:
--=======================
-- Local Script for Dragging Objects
-- Handles detecting mouse input and triggering the DragDetector module.
--=======================
--=======================
-- Module & Player Setup
--=======================
local DragDetector = require(script:WaitForChild("DragDetector")) -- Load DragDetector module
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
--=======================
-- Draggable Object Configuration
-- Determines whether a part is draggable based on attributes.
--=======================
local isDraggable = true -- Default draggable setting (can be overridden per part)
-- Example usage:
-- local basepart = workspace.TestPart
-- basepart:SetAttribute("Draggable", true) -- Make this specific part draggable
-- Alternatively, add the attribute manually via the properties window in Studio.
--=======================
-- Mouse Click Detection
-- Listens for left mouse clicks and checks if the clicked part is draggable.
--=======================
mouse.Button1Down:Connect(function()
local target = mouse.Target
if target and target:IsA("BasePart") then
-- Check if the part has a 'Draggable' attribute, defaulting to `true` if not set
local isDraggable = target:GetAttribute("Draggable") or true
-- Start dragging the part if it is draggable
DragDetector.StartDragging(player, target, isDraggable)
end
end)
Module Script:
--=======================
-- DragDetector Module
-- Handles dragging parts based on the player's mouse input,
-- adjusting distance using right-click + W/S, and locking player movement.
--=======================
local DragDetector = {}
--=======================
-- Services
--=======================
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
--=======================
-- Configuration
--=======================
local MAX_DRAG_DISTANCE = 30 -- Maximum allowed distance from player
local MIN_DRAG_DISTANCE = 10 -- Minimum allowed distance
local MOVE_SENSITIVITY = 1 -- Speed at which the part moves closer/further
local movementLocked = false -- Track if the player’s movement is locked
--=======================
-- Lock / Unlock Player Movement
--=======================
local function lockPlayerMovement(player, lock)
local character = player.Character
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if humanoid then
if lock then
humanoid.WalkSpeed = 0
humanoid.JumpPower = 0
movementLocked = true
else
humanoid.WalkSpeed = 16 -- Default WalkSpeed
humanoid.JumpPower = 50 -- Default JumpPower
movementLocked = false
end
end
end
--=======================
-- UI Generator for Controls
-- Creates a ScreenGui with controls for adjusting the distance (W/S)
--=======================
local function createDistanceAdjustUI(player)
-- Create the ScreenGui
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "DragControlsUI"
screenGui.Parent = player:WaitForChild("PlayerGui")
-- Create the Frame to hold the labels
local frame = Instance.new("Frame")
frame.Name = "ControlFrame"
frame.Size = UDim2.new(0, 200, 0, 100) -- Size of the frame (adjustable)
frame.Position = UDim2.new(1, -210, 1, -110) -- Positioned at the bottom right of the screen
frame.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
frame.BackgroundTransparency = 0.5
frame.BorderSizePixel = 0
frame.Parent = screenGui
-- Create Label for W (Increase distance)
local labelW = Instance.new("TextLabel")
labelW.Name = "LabelW"
labelW.Size = UDim2.new(1, 0, 0.5, 0) -- Takes up half of the frame
labelW.Position = UDim2.new(0, 0, 0, 0)
labelW.Text = "Hold W + Right Click to increase distance"
labelW.TextColor3 = Color3.fromRGB(255, 255, 255)
labelW.TextScaled = true
labelW.BackgroundTransparency = 1
labelW.Parent = frame
-- Create Label for S (Decrease distance)
local labelS = Instance.new("TextLabel")
labelS.Name = "LabelS"
labelS.Size = UDim2.new(1, 0, 0.5, 0) -- Takes up the second half of the frame
labelS.Position = UDim2.new(0, 0, 0.5, 0)
labelS.Text = "Hold S + Right Click to decrease distance"
labelS.TextColor3 = Color3.fromRGB(255, 255, 255)
labelS.TextScaled = true
labelS.BackgroundTransparency = 1
labelS.Parent = frame
-- Return the created ScreenGui for further management if needed
return screenGui
end
--=======================
-- Show/Hide UI based on dragging state
-- Shows the UI when an item is being dragged, hides when not
--=======================
local function toggleUIDuringDrag(screenGui, isDragging)
if isDragging then
screenGui.Enabled = true
else
screenGui.Enabled = false
end
end
--=======================
-- Start Dragging Function
--=======================
function DragDetector.StartDragging(player, targetPart, isDraggable)
-- Ensure the part is draggable before proceeding
if not isDraggable then
return -- Do nothing if the part is not draggable
end
local screenGui = createDistanceAdjustUI(player) -- Create the UI when dragging starts
screenGui.Enabled = false -- Hide the UI initially
-- Validate the target part
if not targetPart or not targetPart:IsA("BasePart") or targetPart.Anchored then --Add in different rule sets to determine which part can be dragged. Currenlty only unAnchored BaseParts can be
return
end
-- Get necessary player objects
local mouse = player:GetMouse()
local character = player.Character
local humanoidRootPart = character and character:FindFirstChild("HumanoidRootPart")
if not humanoidRootPart then return end
-- Dragging state variables
local dragging = true
local lastCharacterPosition = humanoidRootPart.Position
local distanceFromPlayer = (targetPart.Position - humanoidRootPart.Position).Magnitude
-- Clamp distance within allowed range
distanceFromPlayer = math.clamp(distanceFromPlayer, MIN_DRAG_DISTANCE, MAX_DRAG_DISTANCE)
--=======================
-- Distance Adjustment While Holding Right Click + W/S
--=======================
local isHoldingW = false
local isHoldingS = false
-- Function to continuously adjust distance while holding W or S
local function adjustDistance()
lockPlayerMovement(player, true) -- Lock player movement when adjusting
while dragging and (isHoldingW or isHoldingS) do
if isHoldingW then
distanceFromPlayer = math.clamp(distanceFromPlayer + MOVE_SENSITIVITY, MIN_DRAG_DISTANCE, MAX_DRAG_DISTANCE)
elseif isHoldingS then
distanceFromPlayer = math.clamp(distanceFromPlayer - MOVE_SENSITIVITY, MIN_DRAG_DISTANCE, MAX_DRAG_DISTANCE)
end
task.wait() -- Allow continuous movement updates
end
end
--=======================
-- Placeholder for Rotation (Future Implementation)
--=======================
local function RotatePart()
-- TODO: Implement rotation mechanics if needed.
end
--=======================
-- Input Handling for Distance Adjustment
--=======================
local function onKeyDown(input, gameProcessedEvent)
if gameProcessedEvent then return end
if not UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton2) then return end -- Ensure Right Click is held
if input.KeyCode == Enum.KeyCode.W then
isHoldingW = true
task.spawn(adjustDistance) -- Start adjusting continuously
elseif input.KeyCode == Enum.KeyCode.S then
isHoldingS = true
task.spawn(adjustDistance) -- Start adjusting continuously
end
end
local function onKeyUp(input, gameProcessedEvent)
if gameProcessedEvent then return end
if input.KeyCode == Enum.KeyCode.W then
isHoldingW = false
elseif input.KeyCode == Enum.KeyCode.S then
isHoldingS = false
end
-- Unlock player movement if neither key is held
if not isHoldingW and not isHoldingS then
lockPlayerMovement(player, false)
end
end
local keyDownConnection = UserInputService.InputBegan:Connect(onKeyDown)
local keyUpConnection = UserInputService.InputEnded:Connect(onKeyUp)
--=======================
-- Update Function (Moves Part Based on Mouse)
--=======================
local function onUpdate()
if not dragging then return end
-- Adjust for player movement
local characterOffset = humanoidRootPart.Position - lastCharacterPosition
lastCharacterPosition = humanoidRootPart.Position
-- Calculate new position and enforce distance constraint
local targetPosition = mouse.Hit.Position + characterOffset
local direction = (targetPosition - humanoidRootPart.Position).Unit
local clampedPosition = humanoidRootPart.Position + (direction * distanceFromPlayer)
-- Update target part's position
targetPart.Position = clampedPosition
end
local updateConnection = RunService.RenderStepped:Connect(onUpdate)
--=======================
-- Stop Dragging Function
--=======================
local function stopDragging()
if dragging then
dragging = false
updateConnection:Disconnect()
keyDownConnection:Disconnect()
keyUpConnection:Disconnect()
toggleUIDuringDrag(screenGui, false)
end
end
-- Stop dragging on mouse release or player death
mouse.Button1Up:Connect(stopDragging)
humanoidRootPart.AncestryChanged:Connect(stopDragging) -- Stop if player dies
targetPart.AncestryChanged:Connect(stopDragging) -- Stop if part is deleted
toggleUIDuringDrag(screenGui, true)
end
return DragDetector