https://www.roblox.com/library/6541280076/CooldownMap
Managing lots of cooldowns can be a pain, so I made this module to make it easier.
Despite how easy it is to learn, I find that setting up cooldowns in scripts for things such as abilities, spawns, etc can become really messy if you have more than 3 cooldowns, and you often end up repeating the same bits of code over and over throughout the script.
Along with this, often developers use wait(n) and a debounce to control cooldowns. This does work in practice, however the default thread scheduler isn’t great for precise cooldowns like this, and you’re better off saving time() or tick().
CooldownMap, a very simple module, provides a solution to that. It’s so lightweight I can fit it in this devforum post!
To use this, simple require the module and append .new()
, as such
local cooldownMap = require(game.ReplicatedStorage.Modules.CooldownMap).new()
From there, you can use it in a tool, like such :
local Tool = script.Parent
local cooldownMap = require(game.ReplicatedStorage.Modules.CooldownMap).new()
Tool.Activated:Connect(function()
local timeleft = cooldownMap:Check("Test", 2)
if timeleft then
print("Still on cooldown! Time left : " .. )
else
warn("Tool activated!")
end
end)
Example 2 :
So that was pretty simple, so simple that, you wouldn’t even really need this module. But what if you have something a bit more complicated? Here’s an example of what that could look like, originally typed for one of my friends as an example of how to use this module.
In this example, it assumes that it’s going into some sort of jojo game (there are a lot of those on Roblox…), where you have a stand toggle key, and some other attacks. This example applies a global cooldown after toggling the stand, making it so player’s can’t toggle and then attack immediately while the toggle animation is playing. (Of course there would be a lot more to it than this, like not allowing the player to attack if they don’t have their stand out).
local COOLDOWN_TOGGLE = 1
local COOLDOWN_BARRAGE = 4
local COOLDOWN_GLOBAL = 0.5
local KeyAbilities = {
[Enum.KeyCode.E] = function()
if CooldownMap:Check("Global") then return end
if CooldownMap:Check("Barrage", COOLDOWN_BARRAGE) then return end
print("Ora ora!")
end,
[Enum.KeyCode.Q] = function()
if CooldownMap:Check("StandSummon", COOLDOWN_TOGGLE) then return end
CooldownMap:Set("Global", COOLDOWN_GLOBAL) -- lets the animation play first
print("Stand toggled...")
end,
}
UserInputService.InputBegan:Connect(function(userInput, gameProcessed)
if gameProcessed then return end
if KeyAbilities[userInput.KeyCode] then
KeyAbilities[userInput.KeyCode]()
end
end)
Of course there are many other ways you can implement this, but if I had to give some tips, I would advise the following :
-
If you are checking a cooldown first on the client, then playing an animation, visual, and then firing a remote event, I recommend making the cooldown on the server smaller (just by 0.05 seconds is enough really) to prevent any small time discrepancies from causing nothing to happen on the server, while the client thinks it should have. (I read over this again and feel like clarifying, this only happens if a player sends input during a lag spike, causing the client on the cooldown to be out of sync with the one on the server. However under most circumstances, unless the player is triggering the cooldown immediately one after the next, this usually shouldn’t be a problem.)
-
If you want to just check a cooldown without modifying it, simply do not specify a time parameter and it will return true/false without editing any cooldowns.
-
For more complex implemenations like gui buttons that display a cooldown ticker, I recommend a seperate implementation using RunService.Heartbeat delta and subtracting from a seperate value to be used for a visual ticker or color change in said button.
-
In most implementations, adding the cooldown is as simple as doing
if cooldownMap:Check("Example", 1) then return end
-
CooldownMap supports any datatype, but I recommend using strings for readability. Instances will work but can be memory leaked by tables, so be careful with that!
-
For checking cooldowns related to player interaction, I recommend concatenating the player’s UserId with a string saying what the cooldown is for. This could range from anything to a hitbox cooldown to a trade request.
Source :
local CooldownMap = {}
CooldownMap.__index = CooldownMap
local clock = os.clock
function CooldownMap:Check(name, length)
if (not self._cooldowns[name]) or (self._cooldowns[name] < clock()) then
if length then
self._cooldowns[name] = clock() + length
end
return false
else
return self._cooldowns[name] - clock()
end
end
function CooldownMap:Set(name, length)
self._cooldowns[name] = clock() + length
end
function CooldownMap:Destroy()
for k, v in pairs(self._cooldowns) do
self._cooldowns[k] = nil
end
for k, v in pairs(self) do
self[k] = nil
end
end
function CooldownMap.new()
return setmetatable({_cooldowns = {}}, CooldownMap)
end
return CooldownMap
Update Log :
- v1.1
– Now uses os.clock() instead of time()
– AddedDestroy
clean-up function
– Now returns time left on cooldown instead oftrue
Foreword :
Yes, I know making a cooldown or a debounce is easy. The point of this is to make it CLEANER, not easier. If you have a game where the player has up to 8 different abilities (like a lot of jojo/anime fighting games on Roblox nowadays), using a module like this will reduce the your script length and increase readability immensely.
A lot of devs also use wait(n) for their cooldowns, which like I said at the top of the post, can be very problematic on slow servers, since wait isn’t guaranteed to wait the correct amount of time.