How I created a scaling bar HUD for my Unity game project.
Objective: create a scaling “health bar” in your Unity game project UI to visualize the impact of using thrusters, or letting the system “cool down”.
Let me introduce a keyword for this article: core. What is the “core”? In my imagination, the “core” is the energy source inside the Player’s ship. It powers its thrusters and laser weapon systems. The next two articles will be centered around this fictitious “core”, how to measure its temperature level, represent it on the UI, and what happens when the “core” overheats. Let’s start reading…
In my game project, I envisioned the following:
I wanted a scaling bar in my HUD that climbed for as long as the thrusters were activated (Left-Shift key held down by the user).
The slider color is a gradient that goes from green (life is good) to red (critical).
If the Player deactivates his thrusters, the scaling bar descends back to the normal green range (cool-down).
If the Player gets excited and keeps his thrusters engaged beyond an acceptable critical range, the core overheats causing the engines and weapons to shut down in order to allow for a cool-down period, leaving the Player drifting helplessly in space. Bad news!
Step 1 — Setting up the scaling bar elements
For this, I needed to first find (or design) a couple of sprite images. The first sprite represents the border of my “health bar”, while the second sprite is an image that tries to convey to the Player what the scaling bar means. For the border, I found a rectangle with a transparent center and rounded ends, and I then used the sprite of my engine thrust as a “label” for the scaling bar.
Edit: the “border” image is optional. Setting it up with the “fill” in the background gives your game a bit more polish. You can opt to simply use a fill bar with your “thruster label”.
For the border, Right-Click on your Canvas and select UI /Image. Select F in the Scene view to find the newly created Image and view the entire Canvas, and center the Image on the Canvas. This Image will become the border of your scaling bar, so rename it to “Border”, drag into it the source image border sprite, and select Set Native Size.
You then want to Right-Click on your Canvas and select Create Empty. Using the Rect Tool, resize the empty object so it fits inside the Border sprite, and rename it to “ThrustersCoreTempHealthBar”. I know it’s a mouthful, but trust me on this :) You then want to drag your Border (child) into this empty object (parent).
Right-Click on ThrustersCoreTempHealthBar, and select UI / Image. Rename the Image to “Fill”, resize it so it’s slightly smaller than the Border sprite, and change its order to the top in the children so it shows up behind the Border. The last element is to add a final Image, rename it “ThrusterImage” and change its Source Image to the Thruster sprite image. Adjust the Scale as required to fit your needs. Your Hierarchy should look like this:
Select ThrustersCoreTempHealthBar, and Add Component / Slider. Drag the Fill image into the Fill Rect field, and configure the rest of the Slider parameters in the Inspector as shown below:
To ensure the Fill bar scales with the ThrustersCoreTempHealthBar, select the Border and set its Rect Transform Anchor Presets as shown below:
Scale and fine-tune as required for the visual positioning that you want. In my case, I opted to set everything up in the top left corner of my UI.
At this point, we’re pretty much ready to code some behavior to the “health bar” so it reflects when the Player boosts its thrusters. Let’s work on a script to control the level of the bar, or in our case, the temperature of the ship’s core (which sounds much cooler).
Step 2 — Attach a script to control the slider
Create a new script, call it “ThrustersCoreTemp” and attach it to the ThrustersCoreTempHealthBar in the UI. Set up two public variables, one to hold the Slider game object (ThrustersCoreTempHealthBar), and the other to hold the slider fill image game object (Fill).
We then need to create two public methods to set the maximum value of the slider as well as the current value of the slider. The goal is to call these methods from the Player script to set the max value (SetMaxCoreTemp), and to change the current value based on the duration that we use our thrusters. The longer we continuously burn the thrusters, the higher the value (SetCoreTemp), and the longer the slider “grows”. By manipulating the values of the ThrustersCoreTemp script, we allow these changes to be represented in the UI.
Now you can set in the Inspector a default color to the Fill game object, and if you were to leave the script as is, as the slider.value increases, the Fill bar would “grow” as a solid colored bar. In order to provide useful feedback to the player, a better option is to set two colors for the Fill, and interpolate between these two colors as the slider.value changes.
To do this, you need to set the slideFill.color, and use the public declaration Lerp to set the color to green when the slider.value equals 0, or to red when the slider.value climbs to 1. You then interpolate between 0 and 1 by dividing the slider.value by the slider.maxValue. Doing this will allow for a smooth transition between the two preset colors.
Your finished ThrustersCoreTemp script should look like this:
Step 3 — Connecting the Left-Shift key
Let’s now take the time to set up the required variables to achieve the desired behaviors. For now, we’re just going to concentrate on the ones required to move the UI scale as the temperature of the core increases.
Private integer coreTempDecrease is how you will adjust the rate at which the thrusters core temperature will decrease over time once the Left-Shift key is released. This occurs in the void ThrusterCoreLogic() which we will be exploring more in-depth in my next article. For now, just remember that the value stored in this int will determine the rate at which the slider bar will decrease out of the red zone back into the green as the “core temperature” cools down.
Boolean values _hasPlayerThrustersCooledDown and canPlayerUseThrusters check to see if the Player has exceeded the max core temperature (which would render the Player vulnerable by shutting down its thrusters and weapon systems temporarily).
We also need to cache a reference to the ThrustersCoreTemp script in order to get access to its void SetCoreTemp() method. This will allow us to set the starting core temperature at 0 (currentCoreTemp). We also set the maxCoreTemp to 1000 in order to cap the high limit of the slider value.
Again, we use the Start() method to set the currentCoreTemp to 0, to adjust the position of the slider bar by passing that value to the ThrustersCoreTemp script, and to initially turn off the Player’s ability to use its thrusters (in my version of the Space Shooter project, this allows for a “countdown” message to be displayed during which the Player game object remains centered on the game scene).
We then execute CalculateThrustersScale() via the Update().
CalculateThrustersScale() is the method I use to detect when the Left-Shift key is being held down or released. It also checks the state of the _hasPlayerThrustersCooledDown and canPlayerUseThrusters booleans. If either one of these is False (“core” overheated and not sufficiently cooled down, or the Player is in a state where he’s prohibited from using thrusters like in the “countdown” coroutine), then the thrusters won’t be activated.
If, on the other hand, we detect the Left-Shift key depressed, and thrusters are cool enough (True), and the Player is in a stage of the game where he can use thrusters (True), then we execute the PlayerThrustersActivate(5). Releasing the Left-Shift executes the PlayerThrustersDeactivate() method.
“5” represents the value of the (int coreTempIncrease) which sets the rate over time that the slider increases in value (from 0 to 1000 max). So every time the Update() calls CalculateThrustersScale() and the Left-Shift is detected, currentCoreTemp is increased by the value of coreTempIncrease, which then calls the SetCoreTemp method in the thrustersCoreTemp (referenced to the ThrustersCoreTemp script attached to the UI slider).
This is where we check to see if the currentCoreTemp has exceeded the maxCoreTemp integer (set earlier at 1000 when the variable was declared), and if it has, we cap the value at 1000. We do this because if we don’t, the excited Player may keep on holding the Left-Shift key, therefore, increasing the stored value of the currentCoreTemp. This will result in inconsistent elapsed time to return the slider from the “maxed out” reading back to “minimum”.
The final line sets the _speed value to 10.0f which represents the maximum speed that the Player may move (the “speed” Power Up allows for short periods at 8.75f without affecting the core temperature. See the previous article here).
Once the Player releases the Left-Shift key, we execute the method named PlayerThrustersDeactivate(). PlayerThrustersDeactivate() takes into account whether or not the Player collected an active Speed PowerUp. That particular PowerUp, in my rendition, multiplies the default speed (5.0f) by a factor of 1.75f which equates to 8.75f. The Speed PowerUp has a lifespan of 5.0f seconds, and I use the _isPlayerSpeedBoostActive bool to verify its state. If True, it uses the default _speed value of 5.0f and multiplies it by its _speedMultiplier (1.75f). This will ensure the speed of the Player’s ship will remain at 8.75f when the Left-Shift key is released (thrusters aren’t active) while we are still within the 5.0f second lifespan of the Speed PowerUp. If the Speed PowerUp is not active (False) or we have elapsed the 5.0f second lifespan, then the Player’s speed will default back to 5.0f once the thrusters are deactivated (Left-Shift key released).
Remember that the “cooling down” of the core and the associated decrease in the slider value in the UI is done in the void ThrusterCoreLogic() method which will be explored in-depth in my next article.
This is where we’re going to stop for the time being. Join me in my next article to finish off the implementation of the scaling thruster bar into my Unity game project. Thanks for reading :)