Legacy GM Diagonal Life Bars?

MaxLos

Member
Noob question but I could not find any topics regarding this. How do deplete a diagonal life bar properly?

This is the code I was using for horizontal bars:
Code:
var spriteWidth = sprite_get_width(spr_healthbar); //Width of health bar sprite
var spriteHeight = sprite_get_height(spr_healthbar); //Height of health bar sprite
var HealthPercent = obj_player.HP/obj_player.MaxHP; //Percentage of health left

draw_sprite_part(spr_healthbar, 0, 0, 0, spriteWidth*HealthPercent, spriteHeight, 0, 0);
But it doesn't really properly fill/deplete bars that are tilted.


20191009_060918.gif
 

CloseRange

Member
draw_sprite_part will always draw a sprite in this regard.
if your origional sprite is drawn normally (horizontal or vertical like the bar on the left) then you could instead rotate the image and use draw_sprite_part_ext which allows for rotation.
that's the best option.

your other option is to set the image to a texture and use draw_vertex_texture.
However then you run into the problem that your image needs to be a power of 2 and a perfect square so you'd have to scale the image down to the size you want.
The is the less good option.

OR if your healthbar is as simple as the image above then don't use sprites at all. Just use 2 draw triangles for the inside and 4 draw_line to get the border.
 

MaxLos

Member
Well unfortunately my health bar isnt as simple as the image, I was just using it as an example. And I know nothing about textures... But thanks for the heads up about rotating the sprite I think that might actually work so ill try that
 

Yal

🐧 *penguin noises*
GMC Elder
This thing from one of Mike's tech blogs is GMS2-only, but it looks like exactly the use-case you want:
https://www.yoyogames.com/blog/430/dynamic-rendering-masks

Rotating a sprite (and then draw the partial fill with e.g. draw_sprite_general so you can draw a part AND rotate it) probably works for simple cases (e.g. Persona 5 style). If you want something really dynamic like Kingdom Hearts where the healthbar curls around in a circle, you might need to have several different sprites for different fill levels (which will use a lot of VRAM and texture page space, but will make you rip less of your hairs out in frustrating trying to get the clever solution to work).

Another creative solution for weird shapes: use a path, step through it between 0...hp% with path_get_x/path_get_y (path position 1 is the end of the path, position 0 is the beginning), draw small segment sprites along the path from beginning to end position. Easily uses up a lot of processing power, but less memory. The age-old tradeoff between speed and space. A nice border around the path-drawn healthbar could be achieved by taking a screenshot of the filled bar, pasting it into GIMP or Photoshop (did they add copypasting into the GMS2 sprite editor yet?), and making an outline around the shape, then copying that and making a sprite outta it.
 

MaxLos

Member
Welp, rotating the sprite didn't work. I thought I could just rotate a segment of the bar but doing so distorts the pixels and cause of the shape of my bar rotating it at any angle wouldn't make bar fill the outline of the curved sections of the bar anyway (picture of bar below)


bars.PNG


I also really want to avoid having to make multiple indexs for the sprite if i can... So ill try using paths like Yal suggested.
Not too familiar with them, though. How would I go about drawing segments along the path? @Yal
 

Yal

🐧 *penguin noises*
GMC Elder
Have a loop designed to loop from 0 to 1 when the bar is full (so at half health, loop to 0.5). At each such step, use path_get_x and path_get_y to get the path position for the loop value. (Subtract its value for position 0 to get a value that's independent on where the first point is located in the editor). Draw a small "segment" sprite at this position.

Now, there's a whole bunch of kinks left to iron out in this. First of all, the shape of the segment sprite. Circles would be nice, since they have the best coverage, but this will give clear rounded edges. Let's consider a rectangle of width 1 and height equal to the bar's... uh, "acrossicord" instead, with its origin in the center right.
upload_2019-10-10_21-13-41.png

What's so special with this shape? It will face the direction of the path, but if we draw it with an image_xscale > 1, it will stretch backwards. We want to do exactly that: stretch it with the distance between two points on the path. Since it's 1 pixel, you can stretch it by the distance, and it will match perfectly.

Now, we actually need to stretch it more than that sometimes, because of the outer edge of a corner being further away from the previous' point's counterpart than the center point is. This creates a gap.
upload_2019-10-10_21-16-59.png

This gap will be at most the square root of 2 larger than the side of the distance from the center point to the outer point of the bar (so acrossicord / 2 * sqrt(2) = acrossicord/sqrt(2) = acrossicord/1.4), if you're lazy you could just always stretch it with this value (precomputed as a constant for speed) instead of computing it based on the angle.


Finally, the sprites might look a bit like a patchwork of rotated sprites, because that's why it is. You could solve that by have the segment sprites be pure white, and then use a shader or some blend mode trickery to project a nicer texture onto them.
 

Attachments

If you provide a diagram explaining exactly how you created the curves in that healthbar, it could help us help you to come up with the most accurate solutions.

I believe one of the more straightforward things to do would be to use a shader that fills the healthbar based on a gradient texture and a health value. But to do that, we'd need to be able to construct the gradient, which means we need to know how the curve was generated.
 

MaxLos

Member
Ok so.. correct me if im wrong cause I was a little bit confused but here's what I did so far:

First I made a path (not sure if I placed it correctly?)
path health.PNG

Then I made a segment with a width of 1 and the height of the.. ''acrossicord", which is right here, right?
acrossicord.PNG
If so, that gave me a segment size of 1 x 26.

Then I put this in the Draw Event
Code:
path_position = HP/100;
draw_sprite(spr_segment,0,path_get_x(path_health,path_position),path_get_y(path_health,path_position));
Is all of that right so far? @Yal

Also, @flyingsaucerinvasion I didn't make the curves in for the healthbar with code, I just used the fill tool on the outline of the sprite, made it a separate sprite, and tried drawing it with the code in my first post. This is then when I realized it didn't work too well with curves
 
Last edited:

Yal

🐧 *penguin noises*
GMC Elder
path_position = HP/100; draw_sprite(spr_segment,0,path_get_x(path_health,path_position),path_get_y(path_health,path_position));
Nonono, we need rotation and scale as well... more like
Code:
step = 0.01
segment_size = path_get_length(path_health)*step
for(pp = 0; pp < hp/hp_max; pp += step){
  xx = path_get_x(path_health,pp)
  yy = path_get_y(path_health,pp)
  dir = point_direction(xx,yy,path_get_x(path_health,pp+step),path_get_y(path_health,pp+step))
  draw_sprite_ext(spr_segment,0,xx,yy,segment_size,1,dir,c_white,1)
}
 

MaxLos

Member
Nonono, we need rotation and scale as well... more like
Code:
step = 0.01
segment_size = path_get_length(path_health)*step
for(pp = 0; pp < hp/hp_max; pp += step){
  xx = path_get_x(path_health,pp)
  yy = path_get_y(path_health,pp)
  dir = point_direction(xx,yy,path_get_x(path_health,pp+step),path_get_y(path_health,pp+step))
  draw_sprite_ext(spr_segment,0,xx,yy,segment_size,1,dir,c_white,1)
}
Ahhh thank you so much, its working! It does overlap the outline of the bar a little bit but I think I can fix it by myself easy. Thanks again :)
 

Yal

🐧 *penguin noises*
GMC Elder
You could always draw the outline on top, after you draw the bar itself? Should help hiding any jagged edges as well.
 

MaxLos

Member
You could always draw the outline on top, after you draw the bar itself? Should help hiding any jagged edges as well.
Yeah but the entire outline is a sprite itself so it just draws the whole thing over the bar. I could make the the part that the bar is overlapping a sprite and just draw the outline --> bar--> part the bar was overlapping, i guess
 
Top