Warcraft III/WoW style button cooldown

Started by
10 comments, last by Wilhelm van Huyssteen 14 years, 4 months ago
Hey. ive created an interface for my game thats similar to that of Warcraft III. The last thing i want is to gray out any ability buttons that are on cooldown but then have it gradualy return to normal by taking away from the gray area in a clockswise fasion until the ability isnt on cooldown anymore (that explanation wont make any sense unless youve actualy played warcraft III or WoW, i can probely paste some screenshots if nessesary). i have two different ideas. 1) make 100 different grey textures with different amounts of "greyness" and draw the right one over the button depending on how long the remaining cooldown is (this just seems fail) 2) do some math in the fragment shader to darken areas of the button that should be greyd out. ill pass one uniform float indicating the cooldown progress and then work out if the current pixel should be darkend or not. I have no idea how the math for this would work though... i can get the coordinates of the centerpoint of the button and the coordinates of the current pixel in my fragment shader. based on that and the passed "coooldown progress" amount il need to determine wheather or not to darken the pixel. Thnx in Advance! [Edited by - EternityZA on December 10, 2009 1:32:15 PM]
Advertisement
Use a triangle fan, where the fan's shared vertex is at the center of the button. Five stationary verts, one at the 'zero angle' of the button and the rest at the corners of the button. And then one or more animated verts which follow the edges of the button as the cooldown progresses.

Color modulate the triangles so that the triangles 'before' the animated vert(s) are bright, while the ones 'after' the animated vert(s) are dark.

You probably want to duplicate the animated vertex since you'll want a hard edge between the light/dark areas. If you want a soft edge, you can just add more animated vertices using + or - a few degrees, with whatever kind of gradient you want.

(That said, it would be easier to do this with an indexed triangle list than a fan due to the edges where the color modulation needs to change. The 'fan' just illustrates the layout of the triangles)

[Edited by - Nypyren on December 10, 2009 1:29:58 PM]
It would be fairly easy to do this in a pixel shader. As for some ideas on the maths, offset the coordinates so the center of the button is 0,0. Convert coordinates to polar coordinates and color pixels based on the angle. Each pixel will have an angle 0 to 360 (in radians), if your cooldown is at 50% then don't color pixels when angle is < 180 degrees or something.

The only problem with that is to convert you need to use a square root which is best avoided.

I would suggest you generate a single texture before hand with all combinations and just use that. You can generate it as I just described quite simply so no need to manually do it.

If you use different "cooldown states" on a single texture it fits well (10*10) and its not going to be very big, say 24*24 per image and its still only a 240*240 texture and it only needs a single channel.

Your gonna have to fit "no cooldown" and "100% cooldown" and all those inbetween (which is 101) into your texture but I doubt someone will notice a missing 1% anywhere.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

I'd suggest to go with a single texture and play with alpha test to move "the clock".

For instance use the following texture as alpha channel :


And use alpha test to select which fragments pass and which do not.

Do NOT use alpha for blending. Blending would be done using color blending (not alpha blending).

Then simply render a quad with the same size and position as your button.

Advantages :
- easy to implement because you already have the quad of your button => you can use the same vertex buffer (if any),
- the transition shape is very flexible : you can do the typical warcraft-like clock, or you could change the texture to have a vertical fill, or a waterdrop feeling (weak ergonomics but nice graphics), or whatever,
- fast to render and easily supported on all graphics cards : no fancy vertex and/or fragment shaders.

Here is an example of how to use it (pseudo-code, untested) :
void renderCooldownEffect(float elapsedTime, float totalTime){  if (elapsedTime >= totalTime) return; // nothing to do if not on cooldown  float threshold = elapsedTime / totalTime;  // Allow fragments with alpha lower than the threshold  glEnable(GL_ALPHA_TEST);  glAlphaFunc(GL_LESS, threshold);   // Color blending  glEnable(GL_BLEND);  glBlendFunc(GL_ZERO, GL_SRC_COLOR);  // note : the above line is strictly equivalent to :  // glBlendFunc(GL_DST_COLOR, GL_ZERO);  // Bind the clock texture - please upload it as *alpha* texture  glEnable(GL_TEXTURE_2D);  glBindTexture(GL_TEXTURE_2D, clockTexture);  // Configure the texture unit  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);  // Set how much the clock darkens the button.  // The lower the color, the darker the cooldown effect  glColor3f(0.5f, 0.5f, 0.5f);  glBegin(GL_QUADS);    // render here the quad with texture coordinates  glEnd();  // Below lines may be needed to exit the function with a typical GL state.  // It all depends on your app  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);  glDisable(GL_BLEND);  glDisable(GL_ALPHA_TEST);  glDisable(GL_TEXTURE_2D);}
Quote:Original post by vincoof
I'd suggest to go with a single texture and play with alpha test to move "the clock".

For instance use the following texture as alpha channel :


And use alpha test to select which fragments pass and which do not.

Do NOT use alpha for blending. Blending would be done using color blending (not alpha blending).

Then simply render a quad with the same size and position as your button.

Advantages :
- easy to implement because you already have the quad of your button => you can use the same vertex buffer (if any),
- the transition shape is very flexible : you can do the typical warcraft-like clock, or you could change the texture to have a vertical fill, or a waterdrop feeling (weak ergonomics but nice graphics), or whatever,
- fast to render and easily supported on all graphics cards : no fancy vertex and/or fragment shaders.

Here is an example of how to use it (pseudo-code, untested) :
*** Source Snippet Removed ***
HA!

I brilliantly simple and elegant, very creative!

Sorry, no useful ideas from me...
Thnx for all the suggestions. Ive thought about it and now ive come up with this solution in my shaders. It works perfectly but maybe its a bad idea? i dont much about shader performance. These shaders are used for anything 2D in my game so there's more than just the button cooldown code in it so just ignore the rest.

Thnx!

Vertex shader
uniform mat4 completeMatrix;varying vec4 color;uniform vec3 colour_modifier;uniform float brightness_modifier;uniform float transparency_modifier;varying vec4 pos;		void main(){pos = completeMatrix * gl_Vertex; gl_Position = pos;gl_TexCoord[0] = gl_MultiTexCoord0;color = vec4((gl_Color.r + colour_modifier.r)*brightness_modifier,(gl_Color.g + colour_modifier.g)*brightness_modifier,(gl_Color.b + colour_modifier.b)*brightness_modifier,gl_Color.a*transparency_modifier);			};


fragment shader
				private static String MENU_F = uniform sampler2D tex;varying vec4 color;uniform int texturing;uniform vec4 bounds;varying vec4 pos;uniform mat4 completeMatrix;uniform float shadeDegree;void main (void){		vec4 center = completeMatrix*vec4(0,0,0,1);float angle = (atan2(pos.y - center.y, pos.x - center.x) * 180 / 3.141592653589793)-90.0;				if (angle < 0){  angle += 360;}	if(shadeDegree > angle){  color = color - vec4(0.5,0.5,0.5,0);}		if (texturing == 1){  gl_FragColor = texture2D(tex,gl_TexCoord[0].st)*color;	}else{  gl_FragColor = color;}if (pos.x > bounds.z || pos.x < bounds.x || pos.y > bounds.a || pos.y < bounds.y){  gl_FragColor.a = 0.0;}			};


modifying this to yield a smooth edge instead should also not be a problem i guess.
Using arctangent into a fragment shader is a pretty big hit.

Also, computing the center for every fragment while it changes only every quad (not every fragment) is also a hit.

Both computations can be avoided if you encode the result of the equation into a texture, but at the cost of an additional texture fetch in your shader, obviously.

I think it is slightly better to define a texture like the one I posted above. It costs a bit more memory but is probably faster to render and certainly more flexible. Moreover the texture can be viewed within a picture viewer and many softwares will allow you to preview the effect of moving the threshold.
Even the triangle fan thing would be better than a shader.
It's a triangle-fan with 5 unlit/untextured blended triangles.

And use atan/acos/asin/whatever_your_equation_is table for that (you know it will have 100 elements)
Thnx a lot. il redo that properly.
hmmm... i know im clinging to my shader idea but cant it be improved to be as fast as the alternitive? (without binding another texture since that would defeat the purpose :D)

vincoof pointed out that the performance hit comes from
vec4 center = completeMatrix*vec4(0,0,0,1); and
float angle = (atan2(pos.y - center.y, pos.x - center.x) * 180 / 3.141592653589793)-90.0;

In my fragment shader
vec4 center = completeMatrix*vec4(0,0,0,1);
can be taken out completely since i can just pass it as a uniform and maybe my angle calculation
float angle = (atan2(pos.y - center.y, pos.x - center.x) * 180 / 3.141592653589793)-90.0;
can be optimized? maybe by using something faster than atan2?

I just want to make sure before i implement vincoof's idea.

Thnx!

[Edited by - EternityZA on December 12, 2009 12:29:10 AM]

This topic is closed to new replies.

Advertisement