Recently I’ve been curious on how to effectively solve the “awaiting” problem. For awaiting problem, it means an action will execute after a certain amount of time only if it fulfill some criterias, let me give an example:
Let’s say I have a custom Health Bar. I want to replicate exactly what the default Roblox humanoid health bar does. There’s a property called HealthDisplayType and it’s DisplayWhenDamaged by default. Let’s say I want to recreate such effect. When a humanoid is damaged, it will show the Health Billboard UI, then after 5 seconds, it will return to invisible. However, if someone damage the humanoid during that 5 seconds, it will reset the countdown to 5 seconds again.
However this will still disable the UI even if someone damage the humanoid within that 5 seconds!
I know there should be some hacky solution to it, such as using a repeat wait() or while loop, but is there any other efficient ways without such polling? Like setting a boolean variable?
The way I do it is by creating an intValue and every time the player is hit, the intValue will go up by 1, so the code would look like:
HealthChanged:Connect(function()
intValue.Value = intValue.Value + 1
UI.Enabled = true
wait(5)
intValue.Value = intValue.Value - 1
--if the intValue is not 0, the player has been hit another time and we won't disable the UI
--if the intValue is 0, the player has not been hit while the UI was showing so we disable it
if intValue.Value == 0 then
UI.Enabled = false
end
end)
The way I do this is by tracking a unique “tag” for each event. I get this unique tag by simply calling the tick() function.
local healthTag = 0
HealthChanged:Connect(function()
local tag = tick()
healthTag = tag
UI.Enabled = true
wait(5)
if (tag == healthTag) then
UI.Enabled = false
end
end)
Every time the event is fired, it changes the healthTag. After the 5 seconds has elapsed, it checks to see if its own local version of tag still matches with the higher-scope healthTag. If it does, then you know that it hasn’t been fired again. And therefore you know that this was the most recent firing of the event.
It’s not a stateless approach, but I’ve been doing this for years and it works great.
Not that it’s likely to happen, but tick() has a certain resolution depending on the platform, so it wont always return a unique value between calls (see print(tick()==tick())). A more robust solution would be to increment the current value (local tag = healthTag + 1).
Thanks everyone’s solution, all of them works but I think I’ll stick with Crazyman32’s/speeddecoolste’s one as they both require the least amount of work and they are the most efficient one in my opinion.
using some kind of timer library, because any game I make is likely to need one anyway. Every time the health changes the timer is reset to 5 seconds, and once it finally runs out it emits a signal that the GUI script can listen for. There are some timer libraries for Love2D which also uses Lua, but I don’t actually know if those would work with Roblox.
Here’s what that might look like:
local healthBarTimer = Timer.new()
local healthBarFadeDelay = 5
function onHealthChanged()
healthBarTimer:SetTimeLeft( healthBarFadeDelay )
healthBarTimer:Start() --might be unncessesary depending on the timer lib implementation
showHealthBar()
end
function showHealthBar()
UI.Enabled = true
end
function hideHealthBar( )
UI.Enabled = false
end
healthBarTimer.Ended:Connect(hideHealthBar)
I think this is a nicer abstraction than having to deal with promises or tags. It’d also be easy to make it more complicated, like having it poll the time that’s left every frame for the last second of the timer to make a nice fade-out effect.
If no timer library exists, I’d love to make and publish one
If it’s just the resetting that’s an issue I’d of gone with something simple like:
Please note I am on mobile.
Using this code means the UI will only appear and begin it’s countdown if the UIs Enabled was set to fale on the time of damage.
HealthChanged:Connect(function()
if UI.Enabled == false then
UI.Enabled = true
wait(5)
UI.Enabled = false
end
end)