Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm working in Flash AS3, but pseudo-code or any other language is fine.

How can I make a circular progress bar? Akin to the ship bars in Pax Britanica, you can see them in the screenshot here.

Apart from making them frame by frame, is there some obvious solution I'm missing here?

I'm seeking the equivalent of stretching a rectangle for a regular progress bar, or using a simple mask (if such a solution exists).

(Also I can't tag this with circle or progress bar due to lack of rep if anyone could that would be great)

share|improve this question
add comment

4 Answers

up vote 16 down vote accepted

First I'll explain the principle, then the drawing part:

In my example I'm assuming that you have a progress value of 0 to 100 (%), even though anything else will do.

On a straight line, the current position of progress-bar would simply be that progress value.

On a circle you get that position with trigonometry. Any point on a circle is given as:

point.x = r * cos(angle);
point.y = r * sin(angle); 

where r is the radius of the circle. As you see, the angle is the only variable in those equations. That means that you somehow have to integrate your progress into the angle.

The solution is simple: One "run" around the circle equals 2*Pi (or 360 degrees). You have to split that distance into 100 smaller parts, so that when the progress value is 100, the value inside cos() and sin() is 2*pi (or 360 degrees).

You achieve that by simply dividing 2*pi (or 360 degrees) by 100 and multiplying it with the progress:

float step = 2*pi/100;

point.x = r * cos(progress * step);
point.y = r * sin(progress * step);

Now to the drawing part.

I don't know anything about Actionscript3 (or Flash, for that matter), so I can't tell you the exact procedures, but in general there would be two ways to do this:

First, and (in my opinion easier of the two): Draw the partial circle manually as simply a connection of colored lines with a certain thickness (if you don't need a texture on it and a simple color suffices), or draw the circle as a connection of textured quads (if you need texturing).

Second: You draw a picture which represents your fully-loaded circle and load it as a texture, put it on a quad and render it. Then, you again manually draw a partial circle over that quad and only render the part of the textured quad where the circle is rendered in front of it. In OpenGL, for example, you could easily do this using the stencil buffer.

If you want to know how to calculate the different points on your circle to use them to assemble the required polygons/lines, you can take a look at the above equations again:

point.x = r * cos(angle);
point.y = r * sin(angle); 

You can run those inside a for-loop that runs from 0 to your current progress, for example, if it was a circle that consisted of a series of connected dots (in c++ syntax):

float granularity = 2*PI/required_granularity; //determines how smooth your circle will look
float step = 2*PI/100.0f;

list<Vector2> points; //list of all the calculated points
for(float angle=0; angle < progress*step; angle += granularity)
{
    Vector2 point(radius*cos(angle), radius*sin(angle));
    points.push_back(point); //adds the point to the list
}

Now these points will all be centered on the origin of your coordinate system, so you need to move them to whatever position where you want your circle to be. But I think you can figure that one out yourself :P

share|improve this answer
2  
+1 because this is a high-quality answer. –  Randolf Richardson Aug 11 '11 at 4:57
1  
Wow awesome answer for every language, THANK YOU!! –  Adrian Seeley Aug 11 '11 at 11:34
    
This answer is excellent! –  Nick Weaver Apr 17 '12 at 7:32
add comment

Looks like you're wanting to draw an arc. An arc that's thick. I found this article for doing just that in AS3. Check out the demo at the bottom.

share|improve this answer
    
This is probably the method I will use (just because it is AS3 specific) but I accepted the answer that works in every language to help the most people searching. Thank you! –  Adrian Seeley Aug 11 '11 at 11:35
add comment

I haven't tried the following, but it sounds like it should work.

You need two images:

  1. the outer HUD graphic, make the portion you want the progress to show through transparent
  2. a solid color circle image the radius of the progress meter, then cut the bottom half off and make that transparent (it could be a half square really as the HUD portion will cover the rest)

The following assumes your 0% (and 100%) mark is at the 9 o'clock position.


If your progress is 50% or less:

  • Rotate and draw the semi-circle so the desired percent is visible in the top portion
  • Do a rectangular cut of the bottom half area to remove the left starting of the semi-circle showing in the bottom area
  • Draw the top layer HUD graphic

If your progress is > 50%:

  • Draw the semi-circle so that it fills the entire top half (50% mark). You should need no rotation
  • Rotate and draw the semi-circle again to reflect the remaining percentage
  • For example, if you want 60%, draw the first copy to show 50% then rotate and draw the second copy to show another 10%
  • Draw the top layer HUD graphic

I'm not sure how to do this specifically (or if it's even possible) in AS3. I hope this makes sense (oh, and actually works!).

share|improve this answer
add comment

Another method I saw was using shaders.

You only need one image, the entire HUD image, with the progress area in a gradient gray scale. Start at the 0% position (9 o'clock for example) with pure white and gradually progress to black at the 100% position.

You can't have any gray in your HUD portion, including no white or black. If you need to use those colors, then choose another pure color for your gradient scale (a color not used anywhere else).

Have your program pass the shader the progress value in the range of '0.0' to '1.0' (or whatever scale your shader language uses for it's color ranges). You could also pass the shader a progress bar color (and the 'off' color if you wish) if you want the color to change dynamically, such as when empty (or full) to warn the player.

In your shader you evaluate each source pixel, and if it's pure gray (each R,G,B has the same value) then that pixel is part of the progress meter. If it's not just pass the original color through. If it's a progress meter pixel, you have to determine if it should be the 'on' color, or the 'off' color. Just simply check the pixel color (it's pure gray, so you only need to check say the 'red' component) to see if it's above or below the passed progress value. The effect is that any gray color below the progress value will turn on, the others will turn off.

share|improve this answer
1  
OMG, shaders for a circle section... –  Krom Stern Aug 11 '11 at 5:59
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.