ClickDetector firing twice

I’m tired of this engine bug. Literally from the time I started developing to this day, it pisses me off.

I added debounce, set the MaxActivationDistance to 0, disabled the script… None of them helped. It’s always firing twice.

From the doors breaking the matrix:

To surrealistic numbers:
image

It’s not just one script; literally in every single script that fires on MouseClick has this issue. It doesn’t happen in studio but in actual game.

Here’s a template since all of my ClickDetector scripts use the same template. I can’t find something that causes this problem, and that’s what bothers me for years.

local debounce = false
local function example()
	if not debounce then
		debounce = true
		script.Parent.ClickDetector.MaxActivationDistance = 0
		--code here
		script.Parent.ClickDetector.MaxActivationDistance = 10
        debounce = false
	end
end

script.Parent.ClickDetector.MouseClick:Connect(example)

EDIT: Forgot to add that it doesn’t always occur, but sometimes it does.

3 Likes

That’s strange, even if the click detector clicks twice your code should handle that, could we look at the code for the door?

You have to have a task.wait(something) in your script for a debounce to work.

1 Like

I cut out the irrelevant parts (like sound:Play()). It’s pretty much the same thing in result though.

local debounce = false

local function Open()
	if not debounce then
	debounce = true
	ClickOpen1.ClickDetector.MaxActivationDistance = 0
	ClickOpen2.ClickDetector.MaxActivationDistance = 0
	local IsOpen = script.Parent.IsOpen
	if not IsOpen.Value then
		for i = 1,40 do
			Handle:SetPrimaryPartCFrame(Handle.PrimaryPart.CFrame * CFrame.Angles(math.rad(0.2),0,0))
			task.wait()
		end
		Door:SetPrimaryPartCFrame(Door.PrimaryPart.CFrame * CFrame.Angles(0,math.rad(-0.4),0))
		for i = 1,180 do
			Handle:SetPrimaryPartCFrame(Handle.PrimaryPart.CFrame * CFrame.Angles(math.rad(1.3),0,0))
			task.wait()
		end
		task.wait(1.5)
		doorMoving.Value=true
		for i= 1,235 do
			Door:SetPrimaryPartCFrame(Door.PrimaryPart.CFrame * CFrame.Angles(0,math.rad(-0.4),0))
			task.wait()
		end
		doorMoving.Value=false
		task.wait(2)
		IsOpen.Value=true
	else
		doorMoving.Value=true
		for i= 1,236 do
			Door:SetPrimaryPartCFrame(Door.PrimaryPart.CFrame * CFrame.Angles(0,math.rad(0.4),0))
			task.wait()
		end
		doorMoving.Value=false
		local newThread = coroutine.create(function()
			for i = 1,150 do
				Pressure1.Rate = Pressure1.Rate - 0.75
				Pressure2.Rate = Pressure2.Rate - 0.75
				task.wait()
			end
		end)
		coroutine.resume(newThread)
		DoorSmoke.Enabled=true
		task.wait(1)
		DoorSmoke.Enabled=false
		task.wait(1)
		for i = 1,190 do
			Handle:SetPrimaryPartCFrame(Handle.PrimaryPart.CFrame * CFrame.Angles(math.rad(-1.3),0,0))
			task.wait()
		end
		IsOpen.Value=false
	end
	ClickOpen1.ClickDetector.MaxActivationDistance=10
	ClickOpen2.ClickDetector.MaxActivationDistance=10
	end
	debounce = false
end
ClickOpen1.ClickDetector.MouseClick:Connect(Open)
ClickOpen2.ClickDetector.MouseClick:Connect(Open)

It doesn’t always happen (it fires once sometimes), but it’s obvious when it fires twice. It has to do nothing with the script imo, it’s a bug.

Not only the door, as I said, same thing happens in other ClickDetector scripts too.

1 Like

I do have a lot of task.wait() in my scripts, not sure about their placement order in lines tho.

The order of the line does matter when it comes to task.wait() since it yields part of your code for a certain amount of second. Try doing this and see if it fixed your problem:

local debounce = false
local function example()
	if not debounce then
		debounce = true

		task.spawn(function() -- runs code regardless of any yield by task.wait()
				--code here
		end)

		task.wait(Cooldown_Value) -- task.wait() will run regardless of any 
                                  -- wait function inside of "code here"
        debounce = false
	end
end

script.Parent.ClickDetector.MouseClick:Connect(example)
2 Likes

I’ll be trying this, if it gets rid of the problem then I’ll submit it as a solution. It may take a while to make sure since it doesn’t happen too often.

Holy jeez,
Why don’t you use Tweens for those movements? CFraming them like that is going to be kind of jerky, and because you are CFraming there’s a possibility that players won’t be affected by the door’s movement and they’ll clip through the doors.
Tweening a hinge part rotation, and welding the other parts to that would work so much better.

I’ve also used just HingeConstraints for doors and door handles as well, with the Motor set to Servo.
There’s a door model in my profile that uses a touch pad, but you can make it work with a ClickDetector too.

1 Like

I do use Tweens in movements, infact most of the movement scripts on my game are based on Tweens. But when it comes to Tweening another part inside the moving part (talking about the handle on the door here), it doesn’t work since Tweens can’t setPrimaryPartCFrame(). And when you want to weld these parts, it stays fixed in place and doesn’t rotate.

Maybe try this code:

local CLOSED = 1
local OPENING = 2
local OPEN = 3
local CLOSING = 4

local doorState = CLOSED

local function pressed()
    -- Button doesn't do anything while the door is opening and closing
    if (doorState == OPENING) or (doorState == CLOSING) then
        ClickOpen1.ClickDetector.MaxActivationDistance = 0
	    ClickOpen2.ClickDetector.MaxActivationDistance = 0
        return
    end
    
	--ClickOpen1.ClickDetector.MaxActivationDistance = 0
	--ClickOpen2.ClickDetector.MaxActivationDistance = 0
	--local IsOpen = script.Parent.IsOpen
	if doorState == CLOSED then
	    doorState = OPENING
	    ClickOpen1.ClickDetector.MaxActivationDistance = 0
	    ClickOpen2.ClickDetector.MaxActivationDistance = 0
	    
	    -- Code to open door
	    for i = 1,40 do
			Handle:SetPrimaryPartCFrame(Handle.PrimaryPart.CFrame * CFrame.Angles(math.rad(0.2),0,0))
			task.wait()
		end
		Door:SetPrimaryPartCFrame(Door.PrimaryPart.CFrame * CFrame.Angles(0,math.rad(-0.4),0))
		for i = 1,180 do
			Handle:SetPrimaryPartCFrame(Handle.PrimaryPart.CFrame * CFrame.Angles(math.rad(1.3),0,0))
			task.wait()
		end
		task.wait(1.5)
		doorMoving.Value=true
		for i= 1,235 do
			Door:SetPrimaryPartCFrame(Door.PrimaryPart.CFrame * CFrame.Angles(0,math.rad(-0.4),0))
			task.wait()
		end
		task.wait(2)
		
		doorState = OPEN
	    ClickOpen1.ClickDetector.MaxActivationDistance = 10
	    ClickOpen2.ClickDetector.MaxActivationDistance = 10
	else if doorState == OPEN then
	    doorState = CLOSING
	    ClickOpen1.ClickDetector.MaxActivationDistance = 0
	    ClickOpen2.ClickDetector.MaxActivationDistance = 0
	    
		for i= 1,236 do
			Door:SetPrimaryPartCFrame(Door.PrimaryPart.CFrame * CFrame.Angles(0,math.rad(0.4),0))
			task.wait()
		end
		local newThread = coroutine.create(function()
			for i = 1,150 do
				Pressure1.Rate = Pressure1.Rate - 0.75
				Pressure2.Rate = Pressure2.Rate - 0.75
				task.wait()
			end
		end)
		coroutine.resume(newThread)
		DoorSmoke.Enabled=true
		task.wait(1)
		DoorSmoke.Enabled=false
		task.wait(1)
		for i = 1,190 do
			Handle:SetPrimaryPartCFrame(Handle.PrimaryPart.CFrame * CFrame.Angles(math.rad(-1.3),0,0))
			task.wait()
		end
	    
	    doorState = CLOSED
	    ClickOpen1.ClickDetector.MaxActivationDistance = 10
	    ClickOpen2.ClickDetector.MaxActivationDistance = 10
	end
end

ClickOpen1.ClickDetector.MouseClick:Connect(pressed)
ClickOpen2.ClickDetector.MouseClick:Connect(pressed)

There shouldn’t logically be a case where that messes up (assuming you only have one script controlling the door’s state).

1 Like

Haven’t thought of using return to fix this, I will also try this one if @Dragonfable6000’s solution doesn’t help.

1 Like

So use a welded motor between the door handle and the door itself. Neither needs to be anchored.

1 Like

Adding on to this, you have a part at the hinge point that’s anchored and all of the other parts welded to the hinge and set to unanchored. Then you can just tween the hinge (make sure to tween the CFrame property).

2 Likes

I’m also experiencing this issue, seems to happen only with parts that didn’t exist in workspace when the server was launched. For example parts that I cloned from ServerStorage. Here’s my poorly optimized, but well working debounce for you to use. I really like how it works and how its written in the code and the poor optimization doesn’t really matter since it runs once you click it even though there’s an extra variable.

local click_detector_debounce_time = 0.1

function insert_click_detector(parent_of_click_detector)
  local new_click_detector = Instance.new("ClickDetector")
  new_click_detector.MaxActivationDistance = 20
  new_click_detector.Parent = parent_of_click_detector
  return new_click_detector
end

function setup_horn_part(horn_part)
  horn_part.Sound.SoundId = "rbxassetid://"..tostring(horn_part:GetAttribute("Horn_Sound"))
  local horn_part_click_detector = insert_click_detector(horn_part)
  local last_time_click_detector_was_clicked = 0

  print("setup ".. horn_part.Name)
  horn_part_click_detector.MouseClick:Connect(function()
    print("clicked ICD of ".. horn_part.Name)
    if os.clock() - last_time_click_detector_was_clicked < click_detector_debounce_time then return end
    last_time_click_detector_was_clicked = os.clock()

    horn_part.Sound:Stop()
    horn_part.Sound.TimePosition = horn_part:GetAttribute("Horn_Sound_Time_Position")
    horn_part.Sound.Playing = true
  end)
end
1 Like