Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

Why does ContextActionService fire UserInputState.Begin twice on keypad input?

Asked by 7 years ago
Edited 7 years ago

Long Title for such a small thing.

Why does ContextActionService fire UserInputState.Begin twice on keypad keys and once on normal keys? It should only be firing once with Enum.UserInputState.Begin and once with Enum.UserInputState.End when you release a key.

< It does this when you connect a 'normal' key from your keyboard.

Here's my small experimental chunk of local code:

local ContextActionService = game:GetService("ContextActionService")

ContextActionService:BindAction("keypad", print, false, Enum.KeyCode.KeypadFive)
ContextActionService:BindAction("normal", print, false, Enum.KeyCode.H)

Some months ago when I was testing this, it didn't fire UserInputState.End on keypad key release at all, now it fires twice Begin and one End ._.

thanks

2 answers

Log in to vote
1
Answered by 7 years ago
Edited 7 years ago

(Strange... it only fires one Begin and one End for me. I've found the UserInputService to sometimes fire events twice in other scenarios, however.)

To fix when Roblox is sending out duplicate events, use 'debounce'. ex, here is a modified print that will only print its argument if it's not been printed in this time frame before:

do
    local args
    local lastTime = -1
    function printOnce(arg)
        if lastTime ~= workspace.DistributedGameTime then
            args = {}
            lastTime = workspace.DistributedGameTime
        end
        if args[arg] then return end --already printed this
        args[arg] = true
        print(arg)
    end
end
printOnce(1) --Prints 1
printOnce(2) --Prints 2
printOnce(1) --Duplicate, so doesn't print anything
wait()
printOnce(1) --Time has passed, so prints 1

To prevent having to write this code repeatedly, you might use a wrapper function like these:

function CallFuncOncePerTime(func) --pass in the function
    local lastTime = -1 --this local variable is created for each function you pass in
    return function(...) --this is the function to return; it accepts any number of arguments
        if lastTime == workspace.DistributedGameTime then return end --we want the function to do nothing in this condition
        lastTime = workspace.DistributedGameTime --update lastTime
        func(...) --call the original function with any arguments passed in
    end
end
function CallFuncOncePerTimeArg(func) --func will run only if its first argument is unique for this time frame
    local lastTime = -1
    local args
    return function(...)
        if lastTime ~= workspace.DistributedGameTime then --reset args
            args = {}
            lastTime = workspace.DistributedGameTime
        end
        local arg = {...} --get first arg
        arg = arg[1]
        if args[arg] then return end --this is a duplicate; return
        args[arg] = true
        func(...)
    end
end

--Usage example
function test()
    print("Test")
end
test = CallFuncOncePerTime(test)
--or
test = CallFuncOncePerTime(function()
    print("Test")
end

--example 2:
function test(arg)
    print("Test", arg)
end
test = CallFuncOncePerTimeArg(test)
--or
test = CallFuncOncePerTimeArg(function(arg)
    print("Test", arg)
end)

You can thereby modify as many functions as you need so that they all exhibit this behaviour. The idea of a wrapper function is to "wrap" a function in another function. The Roblox article on debounce gives another example of this idea (under "Advanced Notation").

0
I held the keypad key for like 1-2 seconds when it printed another Enum.UserInputState.Begin. Thanks for your debounce suggestion, I'll look into it but don't think I'll make it use time (because there might be some time between the state firing a second time) but just use kind of bools like if table[key] ~= inputstate then table[key] = inputstate code() end! Happywalker 185 — 7y
0
That was important information you didn't mention. Anyway, you're correct that using a table like that will work. I'd use: dict[key] = true/false (recording whether the button is currently pressed down or not), and only fire the 'OnDown' function if it's not already pressed down. chess123mate 5873 — 7y
Ad
Log in to vote
0
Answered by
Dorx86 0
7 years ago

I recomend ContextActionService, its a eaiser version of UserInputServer

local createMobileButton = true -- if u want mobile buttons
local actionName = "CallFuncOncePerTimeArg"

local function onMouseDown(actionName,state,info)
    if state == Enum.UserInputState.Begin then
            function CallFuncOncePerTime()
    local lastTime = -1
    return function(...) 
        if lastTime == workspace.DistributedGameTime then return end
        lastTime = workspace.DistributedGameTime
        func(...) 
    end
end
    elseif state == Enum.UserInputState.End then
        print("Stopped Using Func")
    end
end

contextAction:BindAction(actionName,onMouseDown,createMobileButton,Enum.UserInputType.MouseButton1)

Answer this question