More efficient detection of holding down a button

I’ve seen some topics on how to detect when someone holds down a button (although when I say some I really just mean these two, I couldn’t find anymore: How do I detect if someone holds the mouse down when holding a tool?, How to detect mouse held?). these were good and really helpful but in the end the scripts were too long and I didn’t want to simply copy and paste what they were doing, so I took theirs as a frame a reference and started thinking.

I thought that How to detect mouse held? 's ticket system was good so I created a ticket system and I used my knowledge of repeat until’s to make this:

local players = game:GetService(“Players”)
local uis = game:GetService(“UserInputService”)
local player = players.LocalPlayer
local stand = script.Parent
local re = game:GetService(“ReplicatedStorage”):WaitForChild(“RemoteEvents”):WaitForChild(“Punch”)
local activated = false
local character = player.Character or player.CharacterAdded:Wait()

uis.InputBegan:Connect(function(input, gameProcessed)
local standclone = player.Character:WaitForChild(“aerosmith”) --yes i was making the aerosmith stand
if input.UserInputType == Enum.UserInputType.MouseButton1 then
if gameProcessed == false then --gameprocessed just incase they’re like typing or something
local ticket = 0
repeat
print(“pow”) --make sure it works
ticket = ticket + 1
wait(.001)
until ticket == 20 --if they dont let go or input.UserInputState == Enum.UserInputState.End --i just learned about this
end
end
end)

lets break the important section down:

if gameProcessed == false then

this is used to make sure they dont accidently activate it while typing

wait(.001)

this is actually really important since the game will crash without it, probably shouldve used task.wait though since wait is depracated lol

until ticket == 20 or input.UserInputState == Enum.UserInputState.End

if they never let go it will stop after 20 and instead of doing something like uis.inputended, you dod enum.userinputstate.end since that will only activate once it determines it stops interacting with the screen

1 Like

First and foremost, please format your code correctly using the “Preformatted Text” button, and use the Scripting Support category as this would fit there far better.

Anyway, my personal method of detecting how long a user has held something down is by using RunService.

local tool = script.Parent
local runService = game:GetService("RunService")
local secondGoal, secondAccumulated = 10, 0 --// 10 seconds, 0 accumulated.

function printYay()
    print("Yay!")
end

function countUp(dt)
    secondAccumulated += dt
    if secondAccumulated >= secondGoal then
        --// After 10 seconds has passed, stop counting and proceed with function.
        runService:UnbindFromRenderStep("countUp")
        secondAccumulated = 0
        printYay()
    end
end

--// When tool activated, start counting.
tool.Activated:Connect(function()
    runService:BindToRenderStep("countUp", Enum.RenderPriority.Character.Value, countUp)
end)

--// Manually stop counting & reset timer.
tool.Deactivated:Connect(function()
    runService:UnbindFromRenderStep("countUp")
    secondAccumulated = 0
end)

There may be typos since I wrote this in browser, but I’d say its more optimal since you aren’t stopping your code with a repeat function.
Plus, with this system, task.wait() isn’t needed at all since the code is already limited to the framerate.

Also, wait() is deprecated, you should be using task.wait(), it’s more accurate and if I’m correct, has a lower minimum wait value than wait().
Additionally, you don’t even need to put numbers in, simply having task.wait() will default to the minimum wait value.

My sincerest apologies if I didn’t make it clear :sweat_smile:, I meant it to be a showcase of what I managed to figure out by myself.

Ah, well I wouldn’t say that using a repeat function is exactly… efficient, quite the opposite. They have a multitude of issues, a big one is cancellation.
You have to write more code around cancelling a repeat function to make sure that if it got cancelled, it doesn’t continue the function.
And, as I stated just before, repeat functions, unless used in a task.spawn() function, will pause your code execution entirely for that segment, which means if its not implemented correctly, could cause dropped inputs and general frustration with players.

Plus, I took the way the title was written as asking for a more optimal way, since a LOT of similar posts have similar reading titles.

1 Like

you could just have it detect if the mouse is within the 2D pixel boundaries of the UI you state

when the mouse is clicked detect what UI component the mouse is within by finding which UI the mouse is within (using the UI’s size, UI position and mouse position)

once this is done you store the detected UI in a local variable like local HeldButton

when the mouse is released all you gotta do is use the same exact code and just check if the detected UI the mouse is on is the same as the HeldButton

if efficient meant more perfomant this is probably the best way I could think of