How to use UIS to tell if the player is holding a key for about 1-2 seconds

Well as the title suggests, I’m basically making abilities and I basically want to set it up in a way where if you tap the key, it does a lesser attack, but if held, it’ll send out a larger version of that same attack, I’m not sure how to even detect if a key is held, more so time for how long.

If anybody can let me know how to detect if a key is held that’d mean the world. I can handle the UIS Input and the key and then an “if” statement, I just have 0 clue how to detect a key held.

Here’s some example code.

local holdDuration = 1 -- seconds to hold for

local uis = game:GetService("UserInputService")

local down,time = false,0
uis.InputBegan:Connect(function(i,gp)
	if gp then return end
	if i.KeyCode ~= Enum.KeyCode.F then return end
	
	down,time = true,os.clock()
	task.wait(holdDuration)
	if os.clock()-time < holdDuration then return end -- if user let go of key and then held it down again
	
	print("held")
end)
uis.InputEnded:Connect(function(i,gp)
	if i.KeyCode ~= Enum.KeyCode.F then return end
	down = false
	
	if os.clock()-time > holdDuration then return end
	print('tapped')
end)
1 Like

what do abbreviations like “os” and “gp”, and I imagine “down,time” is how long the key was down for? Sorry I’m not good when it comes to abbreviations. I want to understand and improve, hence why I ask. If you’re busy or it’s too much to explain I entirely understand, and reagrdless, thank you for going out of your way to send me this, means a lot, I can understand this a bit better.

oh wait i just realized that “down,time” is a variable, I had no idea I could create several variables in one, that’s so helpful

so i figured out “os” means nothing much, just roblox’s operating system that also handles time and date, still a little unsure how to use it but it’s a step in the right direction. Thank you once again for telling me about this and helping me, it means a lot.

I had some free time, so here’s another example with some explanatory comments & references.

See the following documentation for reference:

  1. UserInputService - used to observe whether the game has lost the player’s focus (e.g. by alt-tabbing the game) and to track the inputs the player entered

  2. GuiService - used here to determine whether the user opens the Roblox menu by pressing the GuiButton or pressing the Esc key

  3. Task - allows management / scheduling / yielding of threads

    • You should also see coroutine.status which allows you to determine the status of a thread
  4. os, or in this case, os.clock - a high precision clock that’s used here to determine the elapsed time between the user pressing & releasing the key to determine which attack to use

  5. string and string.format - used to format a string with variables (e.g. the elapsed time in this case) to print user-friendly / human-readable information as an example of what’s going on when we’re using the tool (for you to learn & for debugging purposes)


Example with explanatory comments:

local RunService = game:GetService('RunService')
local GuiService = game:GetService('GuiService')
local UserInputService = game:GetService('UserInputService')

-- i.e. how long, in seconds, does the player have
--      to hold this key until they're doing the 'greater' attack
local activationIntervals = {
  [Enum.KeyCode.F] = 3
}

-- i.e. the last input type used by the player to interface
--      with the game
local activeInputType = nil

-- i.e. these describe current state of the attack, and the inputs
--      used to initialise the current attack
local isAttacking = false
local activeInput = nil
local activeInputStarted = nil

-- i.e. this defines the active thread used whilst the user is
--      holding down the attack key (to perform effects / anims whilst holding)
local activeThread = nil

-- i.e. defines all of the connections we've created in this script
--      so we can clean them up at some point
local connections = { }

--[=[
  Takes an Enum.UserInputType and returns a UserInputType that relates to
  of three input types (Touch, Gamepad, Mouse/Keyboard). Returns nil in the case
  of focus-related inputs

  [!] Note: There are some caveats to this, this is just a quick example
            you should look up how to do this effectively

  @param inputType Enum.UserInputType
  @return Enum.UserInputType | nil
]=]
local function parseInputType(inputType)
  if inputType == Enum.UserInputType.Focus then
    return nil
  end

  if inputType == Enum.UserInputType.Touch or inputType == Enum.UserInputType.Gamepad1 then
    return inputType
  end

  return Enum.UserInputType.Keyboard
end

--[=[
  Cancels the active thread

  @return nil
]=]
local function clearActiveInput()
  local thread = activeThread
  activeThread = nil

  if not thread or coroutine.status(thread) == 'dead' then
    return
  end

  task.defer(task.cancel, thread)
end

--[=[
  Cancels the active thread & clears the current
  active input states

  @return nil
]=]
local function stopActiveInputs()
  clearActiveInput()

  activeInput = nil
  activeInputStarted = nil
end

--[=[
  Cleans up the connections & threads associated with
  this tool

  @return nil
]=]
local function cleanupTool()
  if connections then
    for i = 1, #connections do
      local connection = connections[i]
      if connection and connection.Connected then
        connection:Disconnect()
      end
    end

    connections = nil
  end

  stopActiveInputs()
end

--[=[
  An example method to perform some action in a separate thread
  whilst the key is held, e.g. in the case of performing some
  effect

  @param inputObject InputObject
  @return nil
]=]
local function doSomethingWhilstKeyHeld(inputObject)
  -- start doing some animation etc

  -- do some effect or whatever whilst we're still holding the key
  while activeInput and activeInputStarted do
    local elapsed = os.clock() - activeInputStarted
    print(string.format(
      'Still holding the %q key after %.2fs',
      activeInput.Name, elapsed
      ))

    RunService.Heartbeat:Wait()
  end

  -- cleanup after we've stopped being held...

end

--[=[
  An example method for performing a greater attack

  @param inputObject InputObject
  @return nil
]=]
local function doGreaterAttack(inputObject)
  isAttacking = true

  -- e.g. do some animations / effects
  print('do greater attack!')

  -- e.g. resetting attack after it's performed
  task.delay(1, function ()
    isAttacking = false
  end)
end

--[=[
  An example method for performing a lesser attack

  @param inputObject InputObject
  @return nil
]=]
local function doLesserAttack(inputObject)
  isAttacking = true

  -- e.g. do some animations / effects
  print('do lesser attack!')

  -- e.g. resetting attack after it's performed
  task.delay(1, function ()
    isAttacking = false
  end)
end

-- Observes the input by a client
connections[#connections + 1] = UserInputService.InputBegan:Connect(function (inputObject, gameProcessed)
  if gameProcessed or activeInput or isAttacking then
    return
  end

  local keyCode = inputObject.KeyCode
  if keyCode == Enum.KeyCode.F then
    activeInput = keyCode
    activeInputStarted = os.clock()

    activeThread = task.spawn(doSomethingWhilstKeyHeld, inputObject)
  end
end)

-- Observes the input by a client
connections[#connections + 1] = UserInputService.InputEnded:Connect(function (inputObject, gameProcessed)
  if not activeInput or activeInput ~= inputObject.KeyCode or isAttacking then
    return
  end

  local minTime = activationIntervals[activeInput]
  if not minTime then
    warn(string.format(
      'No minimum activation interval for %q, only able to perform a lesser attack without this',
      activeInput.Name
      ))
  end

  local elapsed = os.clock() - activeInputStarted
  stopActiveInputs()

  if minTime and elapsed >= minTime then
    doGreaterAttack(inputObject)
  else
    doLesserAttack(inputObject)
  end
end)

-- Watches for input type changes so you can modify behaviour depending on the device being used
connections[#connections + 1] = UserInputService.LastInputTypeChanged:Connect(function (inputType)
  inputType = parseInputType(inputType)

  if not inputType or inputType == activeInputType then
    -- i.e. in the case of the same input type, or if it's just focus related
    return
  end

  activeInputType = inputType
  stopActiveInputs()
end)

-- Handles loss of focus in the case of the player alt tabbing and/or opening the Esc menu
connections[#connections + 1] = UserInputService.WindowFocusReleased:Connect(stopActiveInputs)
connections[#connections + 1] = GuiService.MenuOpened:Connect(stopActiveInputs)

-- Handles the cleanup of the tool when destroyed, or in this case, we're just waiting for
-- the script to be destroyed instead - you should perform the cleanupTool() method
-- whenever you're no longer wanting to observe the inputs/events associated with this instead
connections[#connections + 1] = script.Destroying:Connect(cleanupTool)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.