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.
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)
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.
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:
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
GuiService - used here to determine whether the user opens the Roblox menu by pressing the GuiButton or pressing the Esc key
Task - allows management / scheduling / yielding of threads
You should also see coroutine.status which allows you to determine the status of a thread
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
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)