Circular health bars
#1 Members - Reputation: 105
Posted 06 June 2011 - 09:23 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?
#2 Members - Reputation: 1336
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.
#3 Crossbones+ - Reputation: 996
Posted 06 June 2011 - 10:22 PM

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 Members - Reputation: 105
Posted 06 June 2011 - 11:15 PM

Thumbnail from The Witcher.
#5 Members - Reputation: 105
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:
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 Members - Reputation: 176
Posted 06 June 2011 - 11:21 PM
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 Members - Reputation: 105
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 Members - Reputation: 105
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 Members - Reputation: 109
Posted 07 June 2011 - 12:20 AM
Abstraction is my choice of words.
Portfolio: http://www.0x3a.com/
Blog: http://blog.0x3a.com/
#11 Crossbones+ - Reputation: 996
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.
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 Members - Reputation: 1121
Posted 07 June 2011 - 06:12 AM
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.
#14 Senior Moderators - Reputation: 1744
Posted 07 June 2011 - 11:47 AM
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 Members - Reputation: 321
Posted 09 June 2011 - 06:43 PM
#16 Members - Reputation: 101
Posted 27 June 2012 - 12:29 PM
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 Members - Reputation: 3813
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.
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.
The voices in my head may not be real, but they have some good ideas!
#19 Crossbones+ - Reputation: 1518
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.
This topic is locked





