TL;DR
Sample code solution is the very last one at the bottom of this answer. Here is a small in-depth explanation of such code.
I've worked on a personal project in which I implemented exactly what you are looking for. In my scenario, I had to draw a bar representing the player character's shield points (instead of HP), and the shield was capable of reloading and even increase over the normal maximum, in a Halo-like way.
My strategy was simple: the variable in charge of storing HP value was one only to keep the game logic simple, while the Draw Event code drew overlayed healthbars of different colors for each base quantity a full gauge could represent.
Simple solution
Let hp_gauge
be the amount of HP a full healthbar can represent, 30 in our case − using a constant or a local variable rather than hard-coded values is slight better practice and more convenient. Since each bar represents intervals, we can draw each bar by telling them how much wide they must be related to their assigned interval:
// Support value
var hp_gauge = 30;
// First bar (interval 0-30)
draw_sprite_ext(sprite931,-1,0,0,(clamp(hp_current-2*hp_gauge,0,hp_gauge)/hp_gauge),1,0,c_red,1);
// Second bar (interval 31-60)
draw_sprite_ext(sprite931,-1,0,0,(clamp(hp_current-hp_gauge,0,hp_gauge)/hp_gauge),1,0,c_yellow,1);
// Third bar (interval 61-90)
draw_sprite_ext(sprite931,-1,0,0,(clamp(hp_current,0,hp_gauge)/hp_gauge),1,0,c_green,1);
When subtracting hp_gauge
once or twice, we are telling each bar to draw their amount assuming the rest of HP will be drawn later by other functions. E.g. if HP is 70, we'll draw a full red bar, then a full yellow bar, finally a foreground green bar scaled at 33%. Also, the function clamp()
forces the returned value between 0 and 30 for us.
However this method forces us to code the drawing functions for every single gauge that can be drawn − this very snippet assumes maximum HP can be 90. Also, we may draw two or more full bars on top of each other, seeing only the last one while the others would be overdrawn and not visible, and thus wasting CPU time and resources.
Some optimization ideas
Instead of drawing every single healthbar, we can work this out in another way; first, we can find two useful values by using mod
and div
expressions:
- Number of full gauges that can be completely filled by the current health value:
var bars = hp_current div hp_gauge;
- Amount of HP that should be represented with a partial (not full) healthbar:
var hp_partial = hp_current mod hp_gauge;
For example, if the player's HP is 70, the previous values will be: bars
= 70 div
30 = 2; and hp_partial
= 70 mod
30 = 10. This means there are two full healthbars to be drawn (2 * 30 = 60 total HP), plus a third bar scaled to represent the remaining 10 HP.
The meaning of hp_partial
is obvious, these are the points the top scaled bar must represent:
// Foreground bar (unknown intervals)
draw_sprite_ext(sprite931,-1,0,0,(hp_partial/hp_gauge),1,0,c_white,1);
Note that here the xscale
value depends on the value of hp_partial
. On the other hand, bars
tells us how many healthbars virtually lie below our front partial bar (and we need to draw just one bar only):
// Background bar (unknown intervals)
if (bars>0)
draw_sprite_ext(sprite931,-1,0,0,1,1,0,c_white,1);
Note that here xscale
is 1 because we know this bar must be drawn entirely, since hp_current
is greater than hp_gauge
. Of course, the background bar must be drawn before the foreground one.
Now that we can draw our bars, how can we deal with colors to give the player an idea of the amount of remaining health? We can solve this issue by using an array to store the colors we want to use for our gauges:
// Colors
barColor[0] = c_red;
barColor[1] = c_yellow;
barColor[2] = c_green;
If bars
is 0, we draw only the scaled bar, whose color may be red for example. If bars
is 1, we have an extra HP gauge, so the bottom one will be red and the top one yellow (respectively indexed as bars-1
and bars
). With this last trick, we can draw our healthbars as follows:
// Healthbar colors
barColor[0] = c_red;
barColor[1] = c_yellow;
barColor[..] = ...; // this array is up to you
// Background bar
if (bars>0)
draw_sprite_ext(sprite931,-1,0,0,1,1,0,barColor[bars-1],1);
// Foreground bar
draw_sprite_ext(sprite931,-1,0,0,(hp_partial/hp_gauge),1,0,barColor[bars],1);
This way you can use as many colors as you wish for different health levels, or even change the code to use the [merge_color()][3]
function for fancier visual effects, although you must care about bars
as an array index not to be out of bounds.
Alternative to using the barColor[]
array
Since bars
tells us what color the healthbar must be, we can get rid of the barColor[]
variable and using a multi-frame sprite, each frame colorized as needed (e.g. frame 0 is red, frame 1 is yellow, ...); finally, we choose what frame to draw instead of picking a color:
// No healthbar colors here
// Background bar
if (bars>0)
draw_sprite_ext(sprite931,barColor[bars-1],0,0,1,1,0,c_white,1);
// Foreground bar
draw_sprite_ext(sprite931,barColor[bars],0,0,(hp_partial/hp_gauge),1,0,c_white,1);
Hope this answer helps, sorry if too long.