Jump to content

  • Log In with Google      Sign In   
  • Create Account


Circular health bars


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • This topic is locked This topic is locked
19 replies to this topic

#1 Lloyd040690   Members   -  Reputation: 105

Like
0Likes
Like

Posted 06 June 2011 - 09:23 PM

So, a friend of mine and I are working out a game, when we came to the design of the HUD. We began discussing a circular bar for health, or experience, or something of the like. After coming up with the design we were discussing the tech behind it, and we can't wrap our minds around a good way to do a circular health bar. We were discussing a stencil buffer, or perhaps a shader, but that seems a bit much for just a GUI item.

Anyone know how to implement this in a simple fashion, or are we going to have to take one of the more complex routes?

Sponsor:

#2 Trienco   Crossbones+   -  Reputation: 2057

Like
0Likes
Like

Posted 06 June 2011 - 10:02 PM

Anyone know how to implement this in a simple fashion, or are we going to have to take one of the more complex routes?


You realize that of the top of my head I could imagine at least three kinds of "circular health bars". An explanation what you're trying to do would help, but chances are that a texture is going to be your friend.
f@dzhttp://festini.device-zero.de

#3 Mussi   Crossbones+   -  Reputation: 1706

Like
5Likes
Like

Posted 06 June 2011 - 10:22 PM

I'm going to assume you don't mean a Diablo style health display and want to achieve a a sort of clock like display. There are a couple of ways to go about this depending on what your goal is. One way is to have a texture that contains only a small slice of the circle and render that using different rotations to make up your health circle. Another way to do this is to use an angle gradient texture and a pixel shader. Here's an example of an angle gradient:

Posted Image

Inside your pixel shader you decide what pixels you should render using a threshold value. So lets say you're at 25% health and you want to display a quarter of a circle, the gradient goes from 1.0 (white) to 0.0 (black), that means you only want the pixels with a 'color' value of 0.75 and up to render. This should give you the desired result after masking the pixels that lie out of the circle.

Did that help?

#4 Lloyd040690   Members   -  Reputation: 105

Like
0Likes
Like

Posted 06 June 2011 - 11:15 PM

Sorry about that, I'm referring to a bar similar to the teal bar in the thumbnail below. It starts on the right side, where it end in a small ball, and fills up along the black part all the way around the creature's head as your character's experience increases.
Posted Image
Thumbnail from The Witcher.

#5 Lloyd040690   Members   -  Reputation: 105

Like
0Likes
Like

Posted 06 June 2011 - 11:18 PM

I'm going to assume you don't mean a Diablo style health display and want to achieve a a sort of clock like display. There are a couple of ways to go about this depending on what your goal is. One way is to have a texture that contains only a small slice of the circle and render that using different rotations to make up your health circle. Another way to do this is to use an angle gradient texture and a pixel shader. Here's an example of an angle gradient:

Posted Image

Inside your pixel shader you decide what pixels you should render using a threshold value. So lets say you're at 25% health and you want to display a quarter of a circle, the gradient goes from 1.0 (white) to 0.0 (black), that means you only want the pixels with a 'color' value of 0.75 and up to render. This should give you the desired result after masking the pixels that lie out of the circle.

Did that help?



That actually helps quite a lot, thanks man.





#6 kdmiller3   Members   -  Reputation: 176

Like
0Likes
Like

Posted 06 June 2011 - 11:21 PM

I don't know if you're going for a "pie-chart" style circle or a partial ring, but you can make either one out of procedural geometry.

The most straightforward way is to draw a bunch of triangles, either a triangle fan for a circle gauge or a triangle strip for a ring gauge. Depending on the size, you may not need all that many segments. You'll want to interpolate the vertex positions for the last gauge segment, though, so you can get fractional values. As an example, consider a ring-shaped gauge with 32 segments (64 triangles) that is 42% full. 32 * 0.42 = 13.44, so the gauge would have 13 completely-filled segments and one partial segment. If the full vertices of that last segment were Vin[13], Vout[13], Vin[14], and Vout[14], you'd use: Vin[13], Vout[13], Vin[13] + (Vin[14] - Vin[13]) * 0.44, and Vout[13] + (Vout[14] - Vout[13]) * 0.44.

You can also draw the gauge as a circle or ring texture applied to square geometry. That cuts the number of triangles you need to draw to a maximum of 5. Computing the positions is a little bit more complicated and involves trigonometry (specifically, the tangent). Here's how I did it for a game I worked on:

// get vertex memory
VertexTL pointList[7];
VertexTL *ppoint = pointList;
unsigned short pointCount = 0;
static unsigned short indexList[15] =
{
	0, 1, 2,
	0, 2, 3,
	0, 3, 4,
	0, 4, 5,
	0, 5, 6
};
unsigned short indexCount = 0;

// generate center point
ppoint->vv.x = (x0 + x1) * 0.5f;
ppoint->vv.y = (y0 + y1) * 0.5f;
ppoint->vv.z = 0.0f;
ppoint->rhw = 1.0f;
ppoint->diffuse = color;
ppoint->specular = 0xFF000000;
ppoint->uv.u = (u0 + u1) * 0.5f;
ppoint->uv.v = (v0 + v1) * 0.5f;
ppoint++;

// generate top point
ppoint->vv.x = (x0 + x1) * 0.5f;
ppoint->vv.y = (y0);
ppoint->vv.z = 0.0f;
ppoint->rhw = 1.0f;
ppoint->diffuse = color;
ppoint->specular = 0xFF000000;
ppoint->uv.u = (u0 + u1) * 0.5f;
ppoint->uv.v = (v0);
ppoint++;

if (curAngle < 0.25f * M_PI)
{
	// one triangle
	pointCount = 3;
	indexCount = 3;

	// get segment fill ratio
	float ratio = 0.5f + 0.5f * tanf(curAngle);

	// generate top right point
	ppoint->vv.x = (x0) + ratio * (x1 - x0);
	ppoint->vv.y = (y0);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u0) + ratio * (u1 - u0);
	ppoint->uv.v = (v0);
	ppoint++;
}
else if (curAngle < 0.75f * M_PI)
{
	// two triangles
	pointCount = 4;
	indexCount = 6;

	// generate top right point
	ppoint->vv.x = (x1);
	ppoint->vv.y = (y0);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u1);
	ppoint->uv.v = (v0);
	ppoint++;

	// get segment fill ratio
	float ratio = 0.5f + 0.5f * tanf(curAngle - M_PI * 0.5f);

	// generate right point
	ppoint->vv.x = (x1);
	ppoint->vv.y = (y0) + ratio * (y1 - y0);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u1);
	ppoint->uv.v = (v0) + ratio * (v1 - v0);
	ppoint++;
}
else if (curAngle < 1.25f * M_PI)
{
	// three triangles
	pointCount = 5;
	indexCount = 9;

	// generate top right point
	ppoint->vv.x = (x1);
	ppoint->vv.y = (y0);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u1);
	ppoint->uv.v = (v0);
	ppoint++;

	// generate bottom right point
	ppoint->vv.x = (x1);
	ppoint->vv.y = (y1);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u1);
	ppoint->uv.v = (v1);
	ppoint++;

	// get segment fill ratio
	float ratio = 0.5f + 0.5f * tanf(curAngle - M_PI);

	// generate bottom point
	ppoint->vv.x = (x1) + ratio * (x0 - x1);
	ppoint->vv.y = (y1);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u1) + ratio * (u0 - u1);
	ppoint->uv.v = (v1);
	ppoint++;
}
else if (curAngle < 1.75f * M_PI)
{
	// four triangles
	pointCount = 6;
	indexCount = 12;

	// generate top right point
	ppoint->vv.x = (x1);
	ppoint->vv.y = (y0);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u1);
	ppoint->uv.v = (v0);
	ppoint++;

	// generate bottom right point
	ppoint->vv.x = (x1);
	ppoint->vv.y = (y1);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u1);
	ppoint->uv.v = (v1);
	ppoint++;

	// generate bottom left point
	ppoint->vv.x = (x0);
	ppoint->vv.y = (y1);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u0);
	ppoint->uv.v = (v1);
	ppoint++;

	// get segment fill ratio
	float ratio = 0.5f + 0.5f * tanf(curAngle - M_PI * 1.5f);

	// generate left point
	ppoint->vv.x = (x0);
	ppoint->vv.y = (y1) + ratio * (y0 - y1);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u0);
	ppoint->uv.v = (v1) + ratio * (v0 - v1);
	ppoint++;
}
else
{
	// five triangles
	pointCount = 7;
	indexCount = 15;

	// generate top right point
	ppoint->vv.x = (x1);
	ppoint->vv.y = (y0);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u1);
	ppoint->uv.v = (v0);
	ppoint++;

	// generate bottom right point
	ppoint->vv.x = (x1);
	ppoint->vv.y = (y1);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u1);
	ppoint->uv.v = (v1);
	ppoint++;

	// generate bottom left point
	ppoint->vv.x = (x0);
	ppoint->vv.y = (y1);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u0);
	ppoint->uv.v = (v1);
	ppoint++;

	// generate top left point
	ppoint->vv.x = (x0);
	ppoint->vv.y = (y0);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u0);
	ppoint->uv.v = (v0);
	ppoint++;

	// get segment fill ratio
	float ratio = 0.5f + 0.5f * tanf(curAngle);

	// generate top left point
	ppoint->vv.x = (x0) + ratio * (x1 - x0);
	ppoint->vv.y = (y0);
	ppoint->vv.z = 0.0f;
	ppoint->rhw = 1.0f;
	ppoint->diffuse = color;
	ppoint->specular = 0xFF000000;
	ppoint->uv.u = (u0) + ratio * (u1 - u0);
	ppoint->uv.v = (v0);
	ppoint++;
}
This code builds an indexed triangle list, though it could be done somewhat more efficiently. The value curAngle is the fill angle in radians; x0, y0, x1, and y1 are the gauge's extents in screen space; and u0, v0, u1, and v1 are its texture coordinates.

#7 Olhovsky   Members   -  Reputation: 105

Like
-1Likes
Like

Posted 06 June 2011 - 11:25 PM

I'm going to assume you don't mean a Diablo style health display and want to achieve a a sort of clock like display.


There are no clock-like displays in diablo to my knowledge. Maybe you're thinking of the health orb that fills up from bottom to top?

#8 Lloyd040690   Members   -  Reputation: 105

Like
0Likes
Like

Posted 07 June 2011 - 12:17 AM

I'm going to assume you don't mean a Diablo style health display and want to achieve a a sort of clock like display.


There are no clock-like displays in diablo to my knowledge. Maybe you're thinking of the health orb that fills up from bottom to top?


re-read what he typed

...you don't mean a Diablo...

.

#9 0x3a   Members   -  Reputation: 109

Like
0Likes
Like

Posted 07 June 2011 - 12:20 AM

Another ( Really simple ) way of doing this would be using frames ;) But this is more amateurish, using the method kdmiller3 described is one I've implemented in the past as well and works quite well.
If I've helped you in any way please push the reputation button, thanks!

Abstraction is my choice of words.
Portfolio: http://www.0x3a.com/
Blog: http://blog.0x3a.com/

#10 Olhovsky   Members   -  Reputation: 105

Like
0Likes
Like

Posted 07 June 2011 - 03:02 AM

re-read what he typed

...you don't mean a Diablo...

.

Whoops! Thanks for the correction :P

#11 Mussi   Crossbones+   -  Reputation: 1706

Like
0Likes
Like

Posted 07 June 2011 - 05:16 AM

Sorry about that, I'm referring to a bar similar to the teal bar in the thumbnail below. It starts on the right side, where it end in a small ball, and fills up along the black part all the way around the creature's head as your character's experience increases.
Posted Image
Thumbnail from The Witcher.


I'd like to clarify that the technique I described works for the shape(or any other irregular circle shape) provided in your example picture, not sure if that was clear from my previous post. Basically you use the angle gradient to get the right portion of the health you wish to display and then you mask pixels to form the shape you want.

#12 Katie   Members   -  Reputation: 1285

Like
0Likes
Like

Posted 07 June 2011 - 06:12 AM

I did something very similar in a pixel shader -- passed in the %age to fill as a parameter to the corners. Each pixel looked to see if it was inside the circle or outside and bailed if so. Then looked to see what angle round the circle it was, compared that to the display %age and picked an appropriate colour.

We also had a version which did a texture fetch based on the angle so we could pass gradients in as textures to use.

We could then put health bars on thousands of screen elements.

#13 kdmiller3   Members   -  Reputation: 176

Like
0Likes
Like

Posted 07 June 2011 - 11:19 AM

That pixel shader technique has the advantage of working with all kinds of crazy shapes, not just circles. You might be able to do the same thing without a shader by using alpha test.

#14 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 07 June 2011 - 11:47 AM

In general, GUI stuff like this isn't done with a clever shader. The most commonly used tech for game GUIs is Flash, using ScaleForm to render it into the game (RAD game tools recently released Iggy, with equivalent functionality). In this case, the health bar -- whatever its shape -- would be an animation, with the player's health level determining which frame was shown. This lets your GUI artist use content creation tools he's comfortable with, and lets him iterate without constant support from a programmer.

Obviously a ScaleForm license is out of the reach of most indie devs, but if you're using UDK, good news -- it's in there. (Crytek might have it available too, but I'm not sure.)

#15 andur   Members   -  Reputation: 543

Like
0Likes
Like

Posted 09 June 2011 - 06:43 PM

I'd just use an alpha-masked texture. Draw out a nice alpha gradient along the path you want your health bar to follow, and build 2 textures one for the completely filled state and one for the completely empty state. Then pass in the % of life that the player has left to the shader, and have it use the empty texture for alpha values greater than this, and the filled texture for alpha values less than this (with perhaps some blending between them around the boundary). This way, you can have any shape that you want.

#16 acontreras89   Members   -  Reputation: 101

Like
0Likes
Like

Posted 27 June 2012 - 12:29 PM

Hello everybody,

first of all, I must apology for posting in such an old topic. However, I've seen this article (and some others around the internet) a hundred times, and tried to learn some HLSL syntax, but I cannot figure out how to turn this idea into code. I have found some interesting stuff in other forums, but most of it deprecated. Since this is the best explanation I've read about how to do it so far, I've decided to ask here: can someone write an example of the pixel shader? Thank you in advance.

Greetings,

Aaron Contreras

#17 SimonForsman   Crossbones+   -  Reputation: 5810

Like
0Likes
Like

Posted 27 June 2012 - 01:35 PM


Sorry about that, I'm referring to a bar similar to the teal bar in the thumbnail below. It starts on the right side, where it end in a small ball, and fills up along the black part all the way around the creature's head as your character's experience increases.
Posted Image
Thumbnail from The Witcher.


I'd like to clarify that the technique I described works for the shape(or any other irregular circle shape) provided in your example picture, not sure if that was clear from my previous post. Basically you use the angle gradient to get the right portion of the health you wish to display and then you mask pixels to form the shape you want.


And you don't have to use an angle gradient either, you can use a spiral gradient, linear gradients, or a custom drawn mask for some really wierd fill patterns.

Also, you don't have to use a pixelshader for it, you can place your mask texture as the alpha channel in the "fill" texture and use a fixed function alpha test.
I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!

#18 6677   Members   -  Reputation: 1058

Like
0Likes
Like

Posted 27 June 2012 - 04:35 PM

Since this is the best explanation I've read about how to do it so far, I've decided to ask here: can someone write an example of the pixel shader? Thank you in advance.

Why not make your own thread, this one was dead a year ago

#19 slicer4ever   Crossbones+   -  Reputation: 3226

Like
0Likes
Like

Posted 27 June 2012 - 06:01 PM

Hello everybody,

first of all, I must apology for posting in such an old topic. However, I've seen this article (and some others around the internet) a hundred times, and tried to learn some HLSL syntax, but I cannot figure out how to turn this idea into code. I have found some interesting stuff in other forums, but most of it deprecated. Since this is the best explanation I've read about how to do it so far, I've decided to ask here: can someone write an example of the pixel shader? Thank you in advance.

Greetings,

Aaron Contreras


i'm not certain about hlsl, but i imagine it shouldn't be too hard to get the idea from a glsl example, going off the second post, i'd draw a rectangle(or w/e shape really), and do something like so in shader:

uniform float Health; //Mapped health between 1-0, where 1 is full health, and 0 is none.
unifom sampler2D HealthTexture;
uniform vec4 g_Color; //global color(generally red for health)

in vec2 TexCoord;
out vec4 f_Color; //final output color
void main(){
   f_Color = texture2D(HealthTexture, TexCoord);
   if(f_Color.r<=1.0f-Health) discard; //discard the fragment if our health is too low, by doing 1-health(1-health so that we map from black->white in terms of loss health)
   //alternativly, instead of discarding, we could do some other color like black, it's up to you.
   f_Color*=g_color; //Now make it w/e color we want.
}

anywho, that's how i in vision it would be done.
Check out https://www.facebook.com/LiquidGames for some great games made by me on the Playstation Mobile market.

#20 rip-off   Moderators   -  Reputation: 7706

Like
0Likes
Like

Posted 28 June 2012 - 07:35 AM

Please do not resurrect old threads. Feel free to create a new thread if you wish.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS