I have a client script that has all the cooldowns and remote triggers for combat moves, but the problem is, moves can be used at the same time, I have a solution to this by adding a check to see if another move is being used by adding a boolean to the script, but there’s no way to implement a wait() to add a cooldown before another move can be used, is there a workaround to this?
Why can’t you use os.time()
to save the last time a move was used, then every time you try to use a move, you compare current time to last used time
Use coroutine code blocks, coroutine code blocks are new Threads that will not affect the code outside the function
Example:
local NewThread = coroutine.create(function()
print("New Thread")
wait(3)
print("Delayed print on new Thread")
end)
print("Print Before Coroutine")
coroutine.resume(NewThread)
print("Print After Coroutine")
- You can also create multiple Coroutine Threads
So if I understood you correctly, it would be used like this?
if Input.KeyCode == Enum.KeyCode.H then
isMove1OnCooldown = true
ClientCooldown = true
local Move1Coroutine = coroutine.create(function()
wait(2.5)
ClientCooldown = false
end)
coroutine.resume(Move1Coroutine)
Move1Remote:FireServer()
If ure trying to make an CoolDown/Debounce system, try making like this:
local DebounceDelay = 3;
local Debounce = false;
if Input.KeyCode == Enum.KeyCode.H then
if not Debounce then
Debounce = true
-- Fire Server
end
end
while true do
wait(DebounceDelay)
Debounce = false;
end
If u want to make a global debounce to moves, you can make a string value instance on the player and manage it on each script
The while loop would create a halt as there is still code below it, I have a cooldown system in place, however it’s just an actual cooldown for the move itself instead of a cooldown to use another move after using a move.
UserInputService.InputBegan:Connect(function(Input, isTyping)
if isTyping or isMove1OnCooldown or game:GetService("Players").LocalPlayer.Character.Humanoid.Health <= 0 or ClientCooldown == true then
return
end
if Input.KeyCode == Enum.KeyCode.H then
isMove1OnCooldown = true
ClientCooldown = true
local Move1Coroutine = coroutine.create(function()
wait(2.5)
ClientCooldown = false
end)
coroutine.resume(Move1Coroutine)
Move1:FireServer()
I also do not want a global debounce to moves, I just want an inbetween move cooldown on the client.
I’m not sure I fully understand what you’re asking, but if you want a debounce that works across all moves, you can do something like the following:
--!strict
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotesByActionKey = {
[Enum.KeyCode.H] = ReplicatedStorage.Remotes.Move1,
[Enum.KeyCode.G] = ReplicatedStorage.Remotes.Move2,
}
local SECONDS_BETWEEN_MOVES = 2
local localPlayer = Players.LocalPlayer
local lastActionAt = 0
local function isAlive()
local character = localPlayer.Character
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
return humanoid and humanoid.Health > 0
end
local function canDoAction(actionKey: Enum.KeyCode)
local isValidMove = remotesByActionKey[actionKey]
local enoughTimePassed = time() - lastActionAt >= SECONDS_BETWEEN_MOVES
return isValidMove and enoughTimePassed and isAlive()
end
-- Returns true if the action completed, otherwise false
local function tryAction(actionKey: Enum.KeyCode)
if not canDoAction(actionKey) then
return false
end
lastActionAt = time()
remotesByActionKey[actionKey]:FireServer()
return true
end
UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessedEvent: boolean)
if gameProcessedEvent then
return
end
if input.UserInputType == Enum.UserInputType.Keyboard then
local success = tryAction(input.KeyCode)
end
end)
Note: It is important to have the same cooldown on the server to avoid exploiters directly firing the remotes to avoid the client cooldown.
Then insert the while loop that manages the Debounce state on a corountine:
local DebounceDelay = 3;
local Debounce = false;
if Input.KeyCode == Enum.KeyCode.H then
if not Debounce then
Debounce = true
-- Fire Server
end
end
local CoroutineDebounceThread = coroutine.create(function()
while true do
wait(DebounceDelay)
Debounce = false;
end
end)
coroutine.resume(CoroutineDebounceThread)
print("The code below the coroutine will not be affected")
This is fine but you’re going to encounter situations in which the ‘Debounce’ state variable is toggled in quick succession.
This code has a problem where the debounce can get flipped back to false in anywhere from 0-3 seconds. That is, your debounce is not guaranteed to be 3 seconds long. This is because your debounce thread is running on a separate thread, so the timer doesn’t start at the same time as the debounce value changes.This shouldn’t be marked as the solution.
If you really wanted to use coroutines for some reason, you could do something like this:
Using coroutines
local UserInputService = game:GetService("UserInputService")
local DEBOUNCE_SECONDS = 3;
local isDebouncing = false;
local startDebounceCountdown = coroutine.wrap(function()
while true do
isDebouncing = true
print("Starting debounce")
task.wait(DEBOUNCE_SECONDS)
isDebouncing = false;
print("Debounce finished")
coroutine.yield()
end
end)
UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessedEvent: boolean)
if gameProcessedEvent then
return
end
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.H then
if isDebouncing then
print("Still debouncing")
return
end
startDebounceCountdown()
-- Fire Server
print("Firing server")
end
end
end)
But that’s totally unnecessary since the InputBegan
event starts a new thread anyway.
You could also accomplish this without coroutines using the task
library, like so:
Using task.delay:
local UserInputService = game:GetService("UserInputService")
local DEBOUNCE_SECONDS = 3;
local isDebouncing = false;
UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessedEvent: boolean)
if gameProcessedEvent then
return
end
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.H then
if isDebouncing then
print("Still debouncing")
return
end
isDebouncing = true
print("Starting debounce")
task.delay(DEBOUNCE_SECONDS, function()
isDebouncing = false
print("Debounce finished")
end)
-- Fire Server
print("Firing server")
end
end
end)
The solution I posted earlier that uses a timestamp check is also a complete solution.
You’re right, I never thought of using this approach.