Fancy Circular Healthbar

Written by me (Anixias).

function draw_circle_hollow(x, y, radius, width)
{
	draw_circle_hollow_quality(x, y, radius, 0, width, 64);
}

function draw_circle_hollow_quality(x, y, radius, offset, width, quality)
{
	draw_primitive_begin(pr_trianglelist);
	for(var i = offset; i < 360 + offset; i += 360 / quality)
	{
		var r = max(0, radius - width);
		var next_i = (i + (360 / quality));
			
		var inner_x = x + lengthdir_x(r, i);
		var inner_y = y + lengthdir_y(r, i);
		var outer_x = x + lengthdir_x(radius, i);
		var outer_y = y + lengthdir_y(radius, i);
			
		var next_inner_x = x + lengthdir_x(r, next_i);
		var next_inner_y = y + lengthdir_y(r, next_i);
		var next_outer_x = x + lengthdir_x(radius, next_i);
		var next_outer_y = y + lengthdir_y(radius, next_i);
			
		// First triangle
		draw_vertex(inner_x, inner_y);				// inner
		draw_vertex(outer_x, outer_y);				// outer
		draw_vertex(next_inner_x, next_inner_y);	// next inner
			
		// Second triangle
		draw_vertex(next_inner_x, next_inner_y);	// next inner
		draw_vertex(outer_x, outer_y);				// outer
		draw_vertex(next_outer_x, next_outer_y);	// next outer
	}
	draw_primitive_end();
}

function draw_healthbar_circular(x, y, radius, amount, backcol, mincol, maxcol, offset, direction, showback, showborder, width)
{
	draw_healthbar_circular_quality(x, y, radius, amount, backcol, mincol, maxcol, offset, direction, showback, showborder, width, 64);
}

function draw_healthbar_circular_quality(x, y, radius, amount, backcol, mincol, maxcol, offset, direction, showback, showborder, width, quality)
{
	if (direction < 0) offset += 180;
	
	quality = max(3, quality);
	
	// Using primitives
	var start_color = draw_get_color();
	
	// Back
	if (showback)
	{
		// Border
		if (showborder)
		{
			draw_set_color(c_black);
			draw_circle_hollow_quality(x + 1, y    , radius, offset, width, quality);
			draw_circle_hollow_quality(x - 1, y    , radius, offset, width, quality);
			draw_circle_hollow_quality(x    , y + 1, radius, offset, width, quality);
			draw_circle_hollow_quality(x    , y - 1, radius, offset, width, quality);
		}
		
		// Back
		draw_set_color(backcol);
		draw_circle_hollow_quality(x, y, radius, offset, width, quality);
	}
	
	// Lerp color
	var r1, g1, b1;
	var r2, g2, b2;
	var r3, g3, b3;
	
	r1 = (mincol & 0x0000FF);
	g1 = (mincol & 0x00FF00) >> 8;
	b1 = (mincol & 0xFF0000) >> 16;
	
	r2 = (maxcol & 0x0000FF);
	g2 = (maxcol & 0x00FF00) >> 8;
	b2 = (maxcol & 0xFF0000) >> 16;
	
	var amt = clamp(amount / 100, 0, 1);
	r3 = lerp(r1, r2, amt);
	g3 = lerp(g1, g2, amt);
	b3 = lerp(b1, b2, amt);
	
	// Border
	if (showborder)
	{
		if (!showback)
		{
			var _x = x;
			var _y = y;
			
			draw_set_color(c_black);
			for(y = _y - 1; y <= _y + 1; y += 2)
			{
				for(x = _x - 1; x <= _x + 1; x += 2)
				{
					draw_primitive_begin(pr_trianglelist);
					for(var i = offset; i < 360 * amt + offset; i += 360 / quality)
					{
						var r = max(0, radius - width);
						var this_i = direction < 0 ? -i : i;
						var next_i = (i + (360 / quality)) * (direction < 0 ? -1 : 1);
						
						var inner_x = x + lengthdir_x(r, this_i);
						var inner_y = y + lengthdir_y(r, this_i);
						var outer_x = x + lengthdir_x(radius, this_i);
						var outer_y = y + lengthdir_y(radius, this_i);
						
						var next_inner_x = x + lengthdir_x(r, next_i);
						var next_inner_y = y + lengthdir_y(r, next_i);
						var next_outer_x = x + lengthdir_x(radius, next_i);
						var next_outer_y = y + lengthdir_y(radius, next_i);
						
						// First triangle
						draw_vertex(inner_x, inner_y);				// inner
						draw_vertex(outer_x, outer_y);				// outer
						draw_vertex(next_inner_x, next_inner_y);	// next inner
						
						// Second triangle
						draw_vertex(next_inner_x, next_inner_y);	// next inner
						draw_vertex(outer_x, outer_y);				// outer
						draw_vertex(next_outer_x, next_outer_y);	// next outer
					}
					draw_primitive_end();
				}
			}
			x = _x;
			y = _y;
		}
		else
		{
			draw_set_color(c_black);
			
			var border_offset = 4;
			var progress = 0;
			
			draw_primitive_begin(pr_trianglelist);
			for(var i = offset; amt > 0 && i <= 360 * ceil(amt * 1000) / 1000 + offset; i += 360 / quality)
			{
				var r = max(0, radius - width);
				var this_i = direction < 0 ? -(i - border_offset) : i - border_offset;
				var next_i = (i + (360 / quality) + border_offset) * (direction < 0 ? -1 : 1);
				
				var inner_x = x + lengthdir_x(r, this_i);
				var inner_y = y + lengthdir_y(r, this_i);
				var outer_x = x + lengthdir_x(radius, this_i);
				var outer_y = y + lengthdir_y(radius, this_i);
				
				var next_inner_x = x + lengthdir_x(r, next_i);
				var next_inner_y = y + lengthdir_y(r, next_i);
				var next_outer_x = x + lengthdir_x(radius, next_i);
				var next_outer_y = y + lengthdir_y(radius, next_i);
				
				if (i + (360 / quality) > 360 * ceil(amt * 1000) / 1000 + offset)
				{
					var amt_within = (amt - progress) / (1 / quality);
					next_inner_x = lerp(inner_x, next_inner_x, amt_within);
					next_inner_y = lerp(inner_y, next_inner_y, amt_within);
					next_outer_x = lerp(outer_x, next_outer_x, amt_within);
					next_outer_y = lerp(outer_y, next_outer_y, amt_within);
				}
				
				// First triangle
				draw_vertex(inner_x, inner_y);				// inner
				draw_vertex(outer_x, outer_y);				// outer
				draw_vertex(next_inner_x, next_inner_y);	// next inner
				
				// Second triangle
				draw_vertex(next_inner_x, next_inner_y);	// next inner
				draw_vertex(outer_x, outer_y);				// outer
				draw_vertex(next_outer_x, next_outer_y);	// next outer
				
				progress += 1 / quality;
			}
			draw_primitive_end();
		}
	}
	
	var progress = 0;
	
	draw_set_color(r3 | (g3 << 8) | (b3 << 16));
	draw_primitive_begin(pr_trianglelist);
	for(var i = offset; amt > 0 && i <= 360 * ceil(amt * 1000) / 1000 + offset; i += 360 / quality)
	{
		var r = max(0, radius - width);
		var this_i = direction < 0 ? -i : i;
		var next_i = (i + (360 / quality)) * (direction < 0 ? -1 : 1);
		
		var inner_x = x + lengthdir_x(r, this_i);
		var inner_y = y + lengthdir_y(r, this_i);
		var outer_x = x + lengthdir_x(radius, this_i);
		var outer_y = y + lengthdir_y(radius, this_i);
		
		var next_inner_x = x + lengthdir_x(r, next_i);
		var next_inner_y = y + lengthdir_y(r, next_i);
		var next_outer_x = x + lengthdir_x(radius, next_i);
		var next_outer_y = y + lengthdir_y(radius, next_i);
		
		// If this is the last iteration before the loop stops
		if (i + (360 / quality) > 360 * ceil(amt * 1000) / 1000 + offset)
		{
			var amt_within = (amt - progress) / (1 / quality);
			next_inner_x = lerp(inner_x, next_inner_x, amt_within);
			next_inner_y = lerp(inner_y, next_inner_y, amt_within);
			next_outer_x = lerp(outer_x, next_outer_x, amt_within);
			next_outer_y = lerp(outer_y, next_outer_y, amt_within);
		}
		
		// First triangle
		draw_vertex(inner_x, inner_y);				// inner
		draw_vertex(outer_x, outer_y);				// outer
		draw_vertex(next_inner_x, next_inner_y);	// next inner
		
		// Second triangle
		draw_vertex(next_inner_x, next_inner_y);	// next inner
		draw_vertex(outer_x, outer_y);				// outer
		draw_vertex(next_outer_x, next_outer_y);	// next outer
		
		progress += 1 / quality;
	}
	draw_primitive_end();
	
	draw_set_color(start_color);
}

Notes

This can be easily modified to use textures, since it uses primitives to render. The border rendering is slightly wrong on the partial circle (the one that shows the amount), but it’s good enough for my purposes. You can use draw_healthbar_circular_quality to actually render non-circular shapes (quality of 3 is a triangle, quality of 4 is either a diamond or rectangle based on the offset you provide, quality of 5 is a pentagon, quality of 6 is a hexagon, etc.).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s