I need help with detecting when a player clicks, double clicks or holds a part that is placed in workspace.
I know i should use tick and mouse.hit.target for it but i don’t exactly know how i would make it so the double click and the click hold dont overlap eachother.
Record when the user first presses the mouse button.
If they press again within DOUBLE_CLICK_GAP seconds → double-click.
If they hold the button down longer than HOLD_THRESHOLD seconds → hold.
Otherwise → single-click.
Because we need to know exactly when the button is released, we can’t rely on ClickDetector.MouseClick. Instead, place this LocalScript in StarterPlayerScripts, use UserInputService to watch mouse down/up, raycast at their cursor to see if they hit our part, then apply the timing logic.
Tip: If you still want the “hand” cursor on hover, drop a ClickDetector on the part—but we won’t use it for our actual click logic.
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local player = Players.LocalPlayer
local camera = workspace.CurrentCamera
local part = workspace:WaitForChild("ClickPart")
local DOUBLE_CLICK_GAP = 0.25
local HOLD_THRESHOLD = 0.4
local RAY_DISTANCE = 100
local pressSession = 0
local lastPressTime = 0
local waitingForSingle = false
local holding = false
local function raycastFromScreen(screenPos)
local ray = camera:ViewportPointToRay(screenPos.X, screenPos.Y)
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Include
params.FilterDescendantsInstances = { part }
return workspace:Raycast(ray.Origin, ray.Direction * RAY_DISTANCE, params)
end
local function onSingle()
print(player.Name, "-> single-clicked")
end
local function onDouble()
print(player.Name, "-> double-clicked")
end
local function onHold()
print(player.Name, "-> is holding")
end
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then return end
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
local screenPos = UserInputService:GetMouseLocation()
local result = raycastFromScreen(screenPos)
if not (result and result.Instance == part) then return end
local now = time()
if now - lastPressTime <= DOUBLE_CLICK_GAP then
pressSession = pressSession + 1
waitingForSingle = false
holding = false
onDouble()
lastPressTime = 0
return
end
pressSession = pressSession + 1
lastPressTime = now
waitingForSingle = true
holding = true
local session = pressSession
task.delay(HOLD_THRESHOLD, function()
if session == pressSession and holding then
waitingForSingle = false
onHold()
end
end)
end)
UserInputService.InputEnded:Connect(function(input, gameProcessed)
if gameProcessed then return end
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
holding = false
local session = pressSession
if waitingForSingle then
task.delay(DOUBLE_CLICK_GAP, function()
if session == pressSession and waitingForSingle then
waitingForSingle = false
onSingle()
end
end)
end
end)