Click, double click and mouse hold on an object in workspace

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.

1 Like

Boost please i still haven’t gotten to a solution for this.

You can think of the logic like this:

  1. Record when the user first presses the mouse button.
  2. If they press again within DOUBLE_CLICK_GAP seconds → double-click.
  3. If they hold the button down longer than HOLD_THRESHOLD seconds → hold.
  4. 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)