Advertisement

How to Code HUD Cooldown Bars

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

HUD cooldown bars are visual elements in game that don't necessarily belong to the game's world, but are an indication for the player of a period of time during which she can or cannot perform an action.

In role-playing games, multiplayer online battle arenas (like League of Legends or DOTA), or even real-time strategy games, these indicators are often critical to the player.

In this tutorial, we will look at how to implement those cooldown bars, whatever your coding language or tool is, relying on pseudo-code and breaking down the mechanics used. The tutorial does not claim to show the best or the only way to implement cooldown bars, but simply analyses and displays a working, practical way to do so.

All of the demos were made with Construct 2 R168 stable version, and can be opened and executed in its free version. All of the examples' source code is commented.

Let's dive in.

The Basic Cooldown Mechanic

A cooldown bar is mostly a visual feedback for the cooldown mechanic itself, and the cooldown mechanic is basically a timer. Consider an action that a character performs. The moment the action is performed, a timer starts, and while this timer counts down, the character cannot perform the action again. That is the cooldown.

From a code perspective, to prevent the action being performed during the cooldown period, a Boolean variable is set to true when the action is performed. When trying to perform your action, the code checks that the value of the Boolean variable is false, and does not allow the action to be performed otherwise. At the end of the timer, the Boolean variable is set back to false, allowing the action to be performed again.

Consider the following example as an illustration of this concept:

Once you click the Action button, the character performs an action, and the Action button is disabled. During the cooldown, at regular intervals the time remaining in the cooldown is displayed. At the end of the cooldown, the character is ready to perform the action again, so the Action button is re-enabled.

Let's have a look at the pseudo-code:

//An object Character has a boolean variable "Cooldown", a numeric variable "cTimer" and another numeric variable "cEndTime"

On clicked Button
    txtHistory.text = txtHistory.text & newline & "The character performs an action"
    Character.cTimer = 0
    Character.cEndTime = txtTimer.text //The text object "Cooldown duration"
    Character.Cooldown = True
    Button.Enabled = False
    
If Character.Cooldown = True
    Character.cTimer = Character.cTimer + dt
    If Character.cTimer >= Character.cEndTime
        txtHistory.text = txtHistory.text & newline & "The character is ready to perform an action"
        Character.cTimer = -1
        Character.Cooldown = False
        Button.Enabled = True
    Else & Every 0.5 seconds
        txtHistory.text = txtHistory.text & newline & "The action is on cooldown. " & Character.cEndTime - Character.cTimer & " seconds of cooldown remaining."

    

The On clicked Button function can only be executed if the button is enabled, and its code is only executed once after the click. That is the user action that starts the timer and sets Cooldown to be true

When the button is clicked, the Character.cEndTime variable is the time when the cooldown period will finish; this value is set based on the value in the txtTimer text object, seen next to the Action button.

Also at this point, the cTimer value is "reset" to 0 since it is a "new" timer, and Cooldown is set to true to allow the second part of the code to kick in. Finally, we disable the button. (We could simply leave it enabled and add another condition to our On clicked Button function checking whether the value of Cooldown is false before continuing, but disabling the button feels like better feedback.)

Note: In the example, and this pseudo-code, there is no error catching to prevent you from entering something other than a number in that text object. Entering anything else will effectively set cEndTime to be 0—so, basically, no cooldown at all.

Line 10 of the above pseudo-code is executed every tick; it checks whether the value of Character.Cooldown is true and, if so, adds the value of dt to the current value of Character.cTimer.

dt, short for "delta time", is a system variable that returns the time that has elapsed between the rendering of the previous frame and the rendering of the current frame. This means that, however powerful the player's computer is, and whatever framerate the game runs at, we can ensure that the cooldown period lasts the same amount of time. (If we defined the cooldown period in terms of a number of ticks or frames that elapsed, the length of the period in seconds would vary on different machines.) The name of this dt variable or its usage may differ according to your coding engine.

If the cooldown is still active, we check whether the current value of cTimer is greater than or equal to cEndTime; if so, we must have reached the end of the cooldown period. We display a bit of feedback, and set Character.Cooldown to false so that this section of code won't execute again until the user clicks on the Action button. We also re-enable the Action button.

If the cooldown is still active, we display feedback explaining this instead. This specific bit of feedback is actually our "cooldown bar" for this text-based example.

So that's a basic cooldown mechanic based on a basic timer. For the rest of this article, let's focus on the cooldown bars themselves, and see how to implement them.

A Basic Cooldown Bar

A cooldown bar is really just a sprite that changes size or visibility over time, during a cooldown period. It is a feedback mechanic for the player to let her know when she'll be able to perform the action again.

Consider the following example :

Click anywhere on the screen to perform the action. Note that the green bar turns red during the cooldown, and grows from a width of 0 back to its original width. As in the basic textual example, you can set the cooldown duration.

In this example, the bar (named CooldownBar) is nothing more than a colored sprite that is stretched to a width of 100 pixels. When the action is performed, its width is set to 0 pixels. Then, every tick that the Cooldown variable is true, the width is set based on the current value of cTimer.

Let's have a look at the pseudo-code:

//Using the same "Character" object as in the basic example, this time the object is visible on screen

On any mouse click & Character.Cooldown = False
    Character.cTimer = 0
    Character.cEndTime = txtEndTimer.text
    Character.Cooldown = True
    CooldownBar.Width = 0
    CooldownBar.AnimationFrame = 1
    
If Character.Cooldown = True
    Character.cTimer = Character.cTimer + dt
    CooldownBar.Width = (CooldownBar.MaxWidth / Character.cEndTime) * Character.cTimer
    
    If Character.cTimer >= Character.cEndTime
        Character.cTimer = -1
        Character.Cooldown = False
        CooldownBar.Width = CooldownBar.MaxWidth
        CooldownBar.AnimationFrame = 0

The cooldown mechanic itself is pretty similar to the one described in the previous example, so let's focus on the CooldownBar itself. The object has two animation frames: a green 32x32px square and a red 32x32px square. It also has a MaxWidth numeric instance variable which is set to 100 (pixels), the actual maximum width of the bar.

Each tick, if Cooldown is true, CooldownBar.width is set to a fraction of CooldownBar.MaxWidth. We determine this fraction by dividing the maximum width of the bar by the cooldown ending time, and then multiplying this result by the current cTimer time.

At the start and end of the cooldown period, we also make sure to change the animation frame accordingly: to red, when the cooldown starts, and then back to green once the cooldown is done.

Improving the Design

We could go a bit further on the visual aspect. Here are some thoughts:

The origin point of CooldownBar is set at its centre, which gives it the feeling of overall growth. If you prefer, you could set this origin point at the left or the right edge of the object, to give it a more "linear" feeling.

This example is very basic; the bar itself is only composed of two frames of different color to reinforce the cooldown aspect. As long as the bar is red, the user understands the action cannot be performed, as red is generally a colour used to mean "stop". But don't forget your colour-blind players! Make sure to use colours that are really different so that even they can tell the difference, or use another method of visual feedback besides the colour change.

You could also add an outline to the bar, a fixed rectangle that would allow the user to better estimate the time remaining in the cooldown period.

Here is a quick illustration of the previous points :

It is still a very basic refilling bar, and there's even more you could add to the visuals. The bar could consist of an animation instead of just two colored frames. The sprite under the bar could be stylized in such a way that its background appears transparent, or you could even add another sprite over it to give a "glass" impression. There are many ways to stylize the cooldown bar to fit your game's design the best.

Skill Buttons Cooldown

In some games, the player has skill buttons at their disposal. These buttons don't just display the cooldown or availability of the skill; they are also an interface for the user to perform the indicated skill (often in addition to some keyboard shortcuts).

Consider this example, in which clicking on one of the buttons will throw the appropriate weapon and also display the cooldown remaining:

As you can see, each skill button will display an "unfilling" black bar and a timer during its cooldown period. The black bar is just a black colored sprite placed over the skill button with an opacity of 45%, and the timer is a text object. Each instance of the skill button has its own instance of these SkillCover and txtSkillTimer objects.

This time, the Cooldown, sTime and sEndTime variables are "attached" to each SkillButton instance. Also, the origin point for the SkillCover black sprite is at its bottom edge.

Consider this pseudo-code:

//Object "SkillButton" with variables "Cooldown" (boolean), "sTime" (numeric), "sEndTime" (numeric), and a specific animation frame to know which instance is being clicked/selected.
//Object "SkillCover" with a variable "Skill" set accordingly to the animation frame of the SkillButton they are related to.
//Object "txtSkillTimer" with a variable "Skill" for the same purpose as above.

On SkillButton clicked & SkillButton.Cooldown = False
    SkillButton.sTime = 0
    SkillButton.Cooldown = True
    Create Proj & ProjImage objects
    ProjImage.AnimationFrame = SkillButton.AnimationFrame //To either throw a dagger or a ninja star
    
    txtSkillTimer.Skill = SkillButton.AnimationFrame
    &
    SkillCover.Skill = SkillButton.AnimationFrame
        Set txtSkillTimer's position to the bottom of SkillButton
        Set SkillCover's position to the bottom of SkillButton
        Set txtSkillTimer in Front of SkillButton
        Set SkillCover behind txtSkillTimer //Still in front of SkillButton
        txtSkillTimer.Visible = True
        SkillCover.Visible = True
        SkillCover.Height = SkillButton.Height

For each SkillButton & SkillButton.Cooldown = True
    SkillButton.sTime = SkillButton.sTime + dt
    
    txtSkillTimer.Skill = SkillButton.AnimationFrame
    &
    SkillCover.Skill = SkillButton.AnimationFrame
        txtSkillTimer.text = SkillButton.sEndTime - SkillButton.sTime
        SkillCover.height = SkillButton.Height - ((SkillButton.Height / SkillButton.sEndTime) * SkillButton.sTime)
    
        If SkillButton.sTime >= SkillButton.sEndTime
            SkillButton.sTime = -1
            SkillButton.Cooldown = False
                    
            txtSkillTimer.Skill = SkillButton.AnimationFrame
            &
            SkillCover.Skill = SkillButton.AnimationFrame
                txtSkillTimer.Visible = False
                SkillCover.Visible = False

        

Here, we select the correct instance of txtSkillTimer and SkillCover . (This is a Construct 2 technique; we determine the correct instances according to the value of Skill, which should correspond to the animation frame of the current SkillButton that was either clicked or selected in the For each loop.)

Unlike with our previous cooldown bars, SkillCover starts out "full", covering the whole height of the SkillButton, and then, during the cooldown, slowly decreases, revealing the image of the button.

To do this, we we give SkillCover a height that matches that of SkillButton to begin with, and then, each frame, subtract (SkillButton.Height / SkillButton.sEndTime) * SkillButton.sTime from this full height. It's basically the same formula we used before to calculate the fraction of the cooldown period that has elapsed, but in reverse.

Some games will format the time differently and allow the player to configure the display she prefers. For example, one minute and forty seconds could be displayed as 1:40 or as 100s. To apply this in your own game, run an If check, before displaying the text of txtSkillTimer, to see what format the player has selected, and format the text accordingly. Some games or players even prefer not to display the time as text at all.

There's more you could do with the SkillCover. You could play with its opacity to obscure the button more or less than 45%. Since it's a sprite that covers another image, you could also play with its blend mode—for example, to desaturate the underlying image and actually reveal its colors more and more as the cooldown passes. You could even consider adding webGL or shader effects to it to fit the rest of your UI or design. As for the cooldown bar, it's up to your imagination and artistic direction.

Conclusion

We've seen that a cooldown bar is a visual feedback for a simple mechanic that prevents an action from being performed for a certain amount of time.

You can use simple sprites and stretch their width or height over time to signal your user that a cooldown is going on. You can display the remaining time in plain text, formatting it either in minutes or in seconds. You can even apply any kind of animation, blend mode or shader effect that will suit the artistic direction of your game.

If you think about it for a second, you can also use this method to control the rate of fire for a weapon: as long as the gun is on cooldown, it won't shoot another projectile, even if the Fire key is down; at the moment the cooldown is complete, another projectile may be fired. No cooldown bar is required in this case.

You can download all of the examples and open them in Construct 2 free edition R168 or later.

I hope you found this article interesting, and I'm looking forward to seeing your own designs of cooldown bars.

Advertisement