• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Legacy GM Is there a better way to draw HP Bar?

Y

yuki

Guest
Hi i'm in bit of a pickle. Generally in the profiler, i'll let things that around 1-5% pass but 15% seems a bit to much. Right now i know what exactly is taking alot of processing and it goes back to my hp bar and drawing it.
I use a for loop to draw my HP bar based on the MaxHP and the current HP. I split my HP Bar into 3 Sections.
The Start The middle and the end. Which the Middle part is looped to accommodate the HP per 1 Pixel. But i also have an energy bar that does the same. After i draw the Bar i draw the actual health in which i also use the loop.

So in total i have around 4 loops
1 to draw the Bar
1 to draw the HP
1 to draw the Energy Bar
1 to draw the Energy Points

I'm wondering I am doing this correctly, or if there is a better way to do it, wondering if it would be better to just draw the Bar and the HP together perhaps? or will that still take the same amount of processing power. This is my code.
Code:
var i, var i2 var i3 var i4;


//Drawing Health
for (i2 = 0; i2 < global.hp; i2 += 1)
{
  
//Set 1
if global.hp>global.maxhp/4
   {
   draw_sprite(HPBar, 0 ,view_xview[1]+33+(i2*2),view_yview[1]+9);
   } 
   else
   {
   draw_sprite(HPBar, 1 ,view_xview[1]+33+(i2*2),view_yview[1]+9);
   } 
}

// Symbol

draw_sprite(HUD1,0,view_xview[1]-15,view_yview[1]-9) //2
draw_sprite(HUDSymbol,image_index,view_xview[1],view_yview[1]+1)


//Drawing HUD
for (i = 0; i < global.maxhp-6; i += 1)
{
 
   if i < global.maxhp-7
   {
   draw_sprite(HUD2, 0 ,view_xview[1]+40+(i*2),view_yview[1]+3); //11
   }
   else
   {
   draw_sprite(HUD2, 1 ,view_xview[1]+40+(i*2),view_yview[1]+3);
   }
    
  
}



//Drawing EnergyBar
for (i4 = 0; i4 < global.energy; i4 += 1)
{
if hudp=0
{
 draw_sprite(NRG, 3 ,view_xview[1]+38+(i4*2),view_yview[1]+23);
}

}

//Drawing EnergyBar GUI
if hudp=0
{
draw_sprite(NRG,0,view_xview[1]+34,view_yview[1]+23)
}


for (i3 = 0; i3 < global.maxe; i3 += 1)
{
//Draw Energy HUD
   if i3 < global.maxe-1
{
  
   if hudp=0
   {
   draw_sprite(NRG, 1 ,view_xview[1]+38+(i3*2),view_yview[1]+23);
   }
 
}
   else
{
   if hudp=0
   {
   draw_sprite(NRG, 2 ,view_xview[1]+39+(i3*2),view_yview[1]+23);
   }
 
}
 
D

Destroy

Guest
Did you consider stretching the sprites you repeatedly draw? It should do the job if the sprite's left column and right column (seeing as they're probably 2 pixels wide) are the same. You should then take a look at draw_sprite_ext(...) (Namely the xscale parameter - instead of drawing copies you would use global.hp*2 as xscale).

If that's not the case, you could have the sprite resource as it appears with lots of health (I think 128 copies next to each other for a 256 wide sprite would be great .. Partly because I love powers of two, partly because it's easy to stack 2^n copies next to each other) and do something along the lines of
Code:
// . . .
var frame;
var width;
for (i2 = 0; i2 < global.hp/128; i2 += 1)
{
 
//Set 1
    if global.hp>global.maxhp/4
        frame = 0;
    else
        frame = 1;
    width = min(256, global.hp - 128*i2)
    draw_sprite(HPBar, frame, 0, 0, width, sprite_get_height(HPBar), view_xview[1]+33+(i2*256), view_yview[1]+9);
}
// . . .
Let me know if anything is unclear!
 

Yal

šŸ§ *penguin noises*
GMC Elder
I'm wondering I am doing this correctly, or if there is a better way to do it, wondering if it would be better to just draw the Bar and the HP together perhaps?
Your main problems here are using a for loop (which easily gets slow in GML, especially in the draw event), and issuing many "small" draw calls (the pipeline between CPU and GPU is a known bottleneck, so ideally telling the GPU to draw tons of stuff within a single draw call is more efficient than sending it hundreds of draw requests).

Some ideas...
  • draw_sprite_part() plus a set healthbar size could work pretty well, you only need to draw 2 quads this way (one being the entire 'empty' bar sprite, the second being a part of the sprite with a 'full' subimage). This should let you go from hundreds of draw calls to 8.
  • Drawing rectangles and other vector shapes is slower than drawing bits of a texture, but not too much.
  • You could make a model (or vertex buffer in GMS2) of the healthbar, and then use transformations to flatten it along the X-axis when drawing. Kinda overkill but lets the GPU do all the maths work.
 
Y

yuki

Guest
Did you consider stretching the sprites you repeatedly draw? It should do the job if the sprite's left column and right column (seeing as they're probably 2 pixels wide) are the same. You should then take a look at draw_sprite_ext(...) (Namely the xscale parameter - instead of drawing copies you would use global.hp*2 as xscale).

If that's not the case, you could have the sprite resource as it appears with lots of health (I think 128 copies next to each other for a 256 wide sprite would be great .. Partly because I love powers of two, partly because it's easy to stack 2^n copies next to each other) and do something along the lines of
Code:
// . . .
var frame;
var width;
for (i2 = 0; i2 < global.hp/128; i2 += 1)
{
 
//Set 1
    if global.hp>global.maxhp/4
        frame = 0;
    else
        frame = 1;
    width = min(256, global.hp - 128*i2)
    draw_sprite(HPBar, frame, 0, 0, width, sprite_get_height(HPBar), view_xview[1]+33+(i2*256), view_yview[1]+9);
}
// . . .
Let me know if anything is unclear!
Hmm I never thought of doing it that way(stretching the HPBar), that sounds like it might just work and probably really efficient. I'm looking to avoid using set HP bars so i can have some more room on the texture pages. I'll give it a shot and let you know it works.
Your main problems here are using a for loop (which easily gets slow in GML, especially in the draw event), and issuing many "small" draw calls (the pipeline between CPU and GPU is a known bottleneck, so ideally telling the GPU to draw tons of stuff within a single draw call is more efficient than sending it hundreds of draw requests).

Some ideas...
  • draw_sprite_part() plus a set healthbar size could work pretty well, you only need to draw 2 quads this way (one being the entire 'empty' bar sprite, the second being a part of the sprite with a 'full' subimage). This should let you go from hundreds of draw calls to 8.
  • Drawing rectangles and other vector shapes is slower than drawing bits of a texture, but not too much.
  • You could make a model (or vertex buffer in GMS2) of the healthbar, and then use transformations to flatten it along the X-axis when drawing. Kinda overkill but lets the GPU do all the maths work.
I loke the sound of that, i might give that a shot after the first one that i'll try and compare to see which one would be better for the game thanks for the suggestions! If i have any difficulties or questions ill be sure o ask!
 
Y

yuki

Guest
Hey guys, so im back with some results sorry for taking so long. I did draw Sprite Stretched for the HP Bar and the middle part for my HUD container. The issue now is i cant get it to connect properly with the end of the HUD. What i am trying to do is make it dynamic in case i want to have increased/added HP. But the main issue is that the end doesn't connect properly with the middle part, Everytime i increase it the middle part just gets farther and farther away from the end.
Right now i am starting off the game with a max HP of 16. I have the Middle part and the end part into 1 sprite and just call upon whichever one is needed. The End part and the start can hold up to 4 HP so i left a bit of space for the Container to make sure that it doesn't overlap with the end. Here is how it looks so far. Basically i want it always to be at the end of the maxhp. due to the stretching i cant really tell if i am drawing it correctly, it just looks like it does.

Code:
var hx=view_xview[1]+40+(global.maxhp+4)
draw_sprite(HUD2,1,hx,view_yview[1]+3);
draw_sprite_stretched(HUD2, 0 ,view_xview[1]+40,view_yview[1]+3,20*(global.maxhp-6),20);
 
Last edited by a moderator:

kupo15

Member
Another option you may want to consider is using a surface to draw your hud and only update the surface when a value changes.
Clever, I may steal this

Yuki, can you show a picture of your HP bar? I think the draw_part method is the best way over the stretched unless your HP bar is very basic graphically, as in a single color. I still think the draw_part is best
 

Zodaris

Member
It's hard to see what it is you're trying to do without screenshots (for me at least) But I'll try to make some suggestions based on what I can tell. It sounds like you're trying to draw a health bar that happens to be shown as 3 chunks.
1st - If you're wanting to stretch your image to fit, I'd personally suggest draw_sprite_ext() as that uses percent (between 0 and 1) instead of flat values. What this means is you can simply use something like currenthp/maxhp to get the size you want. (or for each section (currenthp - lower sections) / (maxhp - lower sections) then just put that into a max() function with 0 so that you don't get your health bar growing in the other direction as you lose health beyond each section.)
2nd - You can draw the full sprite over the full area of your hp bar, then place a sprite on top that is the same color as the background and/or border of the hp bar. That way it gives the illusion of it being segmented without it actually being so. Much easier to code, but depending on the size of the gaps and how health and damage works could turn out funny.

NOTE: As mentioned above, if you're health indicator isn't just a simple color, my 1st suggestion kinda goes out the window. ALSO, there is an actual healthbar command that uses a percent between 0-100 that may work. However, it is limited in it's flexibility but it could still help (like it doesn't even need a sprite to work!).
 
Y

yuki

Guest
Thanks for the suggestions guys, i eventually got it to work, not sure why it works but it works so i wont try to fix it.
this is what i did

Code:
var exh=view_xview[1]+39+(global.maxe*2)-2
granted after changing a bunch of things with the drawing , it dropped from a previous of 15% to 6% i did want to try to get it lower but with the amount of stuff that is needed to draw i think that i might have to settle for it there. I'm not sure how else i can lower it, since my HUD does draw up alot of things.
 
Top