Creating A Circular/Radial HP Bar Like Kingdom Hearts

From this post: UIGradient [LIVE] - #147 by UIScript
Someone earlier messaged me on how I made this; it’s actually pretty simple, and that’s using the new UIGradient.

UIGradient is in beta, meaning this won’t work in a live client. Not to mention only supports linear gradients (for now?), but what I’ve done is used rotation + transparency to achieve this effect.

Now, I’ll try my best to explain this, so bare with me.

First, I’ve created a corner piece of the health bar. The circle was originally going to consist of 3 parts, TopLeft, TopRight, and BottomRight, but I decided for my sanity just to use TopLeft and rotate it.
120topleft
The image is 500x500, and the thickness of the bar is 120 pixels. If we combine the 3 corner parts, it will result in a 1000x1000 image, translating the thickness to 0.12 in scale if a frame is parented inside a 1:1 frame (which we will use for the horizontal bar).

Now my experiment comes in.

By rotating the UIGradient (from 0 to -90) and by using this Transparency pattern (NumberSequence), you can line up to show a perfect half cut in transparency. The points in the middle are separated by 0.001. Since we know UIGradient rotates by the edges of the frame, setting the rotation to, say -80, will result in a weird skewed image.

To fix that, you’ll have to move the keypoints accordingly to the scale of the rotation (0 to -90).

That’s pretty much all there is to it. We then use math to calculate the rotation and the keypoints position for all 3 corners.
This part will be confusing to a lot of people, and I’m not sure if I can explain this clearly, so I’ve went on ahead and uncopylocked an example that you can grab and check out for yourself:

Going off by what I’ve already created, I’ve made a variable called circleMaxHealth (how much max health will the whole circle hold) which will be the base on how much we need to scale the bar.
We will also work from BottomRight → TopRight → TopLeft to update the visual accordingly.

First, we need to get the minimum health value of each corners based on our circleMaxHealth.

local circleMaxHealth = 100

local function UpdateHud()
	local circleAlpha = circleMaxHealth / 3 -- Meaning 33.333 health per corner piece
	-- Minimum health values of the corner pieces
	local topRightAlpha = circleMaxHealth * (1/3) -- 33.333, same value as circleAlpha but here for the sake of consistency and readability
	local bottomRightAlpha = circleMaxHealth * (2/3) -- 66.666
end

Great, we can now check the HP and compare it to our alphas.

local circleMaxHealth = 100
local Health, MaxHealth = 200, 200

local HealthBar = script.Parent:WaitForChild("HealthBar")

local function UpdateHud()
	local circleAlpha = circleMaxHealth / 3
	local topRightAlpha = circleMaxHealth * (1/3)
	local bottomRightAlpha = circleMaxHealth * (2/3)
	-- Start the checking of health
	if Health <= circleMaxHealth then -- If health is below or equal to circleMaxHealth, then we update the curved bars
		Health.Bar.Size = UDim2.new(0, 0, 0.12, 0) -- The horizontal bar HAS to be invisible if our health is below circleMaxHealth
		if Health >= bottomRightAlpha then -- If health is above bottomRightAlpha (66.666)
			-- TopLeft and TopRight HAS to be visible
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.TopRight.UIGradient.Transparency = NumberSequence.new(0, 0)
			
		elseif Health >= topRightAlpha then -- If health is above or equal to topRightAlpha (33.333)
			-- TopLeft HAS to be visible and BottomRight HAS to be invisible
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(1, 1)
			
		else -- If health is pretty much below topRightAlpha (33.333)
			-- TopRight and BottomRight HAS to be invisible
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(1, 1)
			
		end
	else -- The curved bars HAS to be visible if our health is above circleMaxHealth
		HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
		HealthBar.TopRight.UIGradient.Transparency = NumberSequence.new(0, 0)
		HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(0, 0)
		-- update horizontal bar
	end
end

Perfect, we have our health check and comparison structure down, now it’s time to update the UIGradient accordingly. Instead of writing the same code all over again, we will turn it into a function instead later.

We mentioned earlier that we have to line up the UIGradient’s rotation and transparency pattern. To do that, we have to get the alpha in order to get the position and rotation scale. Since we know each curved bar is 1/3 of circleMaxHealth, we have to get a value between 0-33.333.

local circleMaxHealth = 100
local Health, MaxHealth = 200, 200

local HealthBar = script.Parent:WaitForChild("HealthBar")

local function CurvedBarResize(bar, alpha)
	-- code that will resize bar
end

local function UpdateHud()
	local circleAlpha = circleMaxHealth / 3
	local topRightAlpha = circleMaxHealth * (1/3)
	local bottomRightAlpha = circleMaxHealth * (2/3)
	if Health <= circleMaxHealth then
		Health.Bar.Size = UDim2.new(0, 0, 0.12, 0)
		if Health >= bottomRightAlpha then
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.TopRight.UIGradient.Transparency = NumberSequence.new(0, 0)
			local alpha = (Health.Value - bottomRightAlpha) / circleAlpha -- Gets the 0-33.333 value of BottomRight and divides by circleAlpha
			CurvedBarResize(HealthBar.BottomRight, alpha)
		elseif Health >= topRightAlpha then
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(1, 1)
			local alpha = (Health.Value - topRightAlpha) / circleAlpha -- Gets the 0-33.333 value of TopRight and divides by circleAlpha
			CurvedBarResize(HealthBar.TopRight, alpha)
		else
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(1, 1)
			local alpha = Health.Value / circleAlpha -- Gets the 0-33.333 value of TopLeft and divides by circleAlpha
			CurvedBarResize(HealthBar.TopLeft, alpha)
		end
	else
		HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
		HealthBar.TopRight.UIGradient.Transparency = NumberSequence.new(0, 0)
		HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(0, 0)
		-- update horizontal bar
	end
end

And this is it, the final step to achieve the curved bars. I’ve mentioned earlier that the points in the middle of the NumberSequence are separated by a gap of 0.001, this is because in my testings, it resulted in ordering them randomly.

local circleMaxHealth = 100
local Health, MaxHealth = 200, 200

local HealthBar = script.Parent:WaitForChild("HealthBar")

local function CurvedBarResize(bar, alpha)
	bar.UIGradient.Rotation = -90 * (1 - alpha) -- (1 - alpha) since we go backwards (from 0 to -90)
	-- NumberSequence has to be in order, so I did some checks
	if alpha - 0.001 <= 0 then -- We don't want a negative time value or 2 values on time = 0
		bar.UIGradient.Transparency = NumberSequence.new(1, 1) -- The bar is empty
	elseif alpha == 1 then -- We don't want to 2 values on time = 1
		bar.UIGradient.Transparency = NumberSequence.new(0, 0) -- The bar is full
	else
		bar.UIGradient.Transparency = NumberSequence.new({
			NumberSequenceKeypoint.new(0, 0),
			NumberSequenceKeypoint.new(alpha - 0.001, 0),
			NumberSequenceKeypoint.new(alpha, 1),
			NumberSequenceKeypoint.new(1, 1)
		})
	end
end

local function UpdateHud()
	local circleAlpha = circleMaxHealth / 3
	local topRightAlpha = circleMaxHealth * (1/3)
	local bottomRightAlpha = circleMaxHealth * (2/3)
	if Health <= circleMaxHealth then
		Health.Bar.Size = UDim2.new(0, 0, 0.12, 0)
		if Health >= bottomRightAlpha then
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.TopRight.UIGradient.Transparency = NumberSequence.new(0, 0)
			local alpha = (Health.Value - bottomRightAlpha) / circleAlpha
			CurvedBarResize(HealthBar.BottomRight, alpha)
		elseif Health >= topRightAlpha then
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(1, 1)
			local alpha = (Health.Value - topRightAlpha) / circleAlpha
			CurvedBarResize(HealthBar.TopRight, alpha)
		else
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(1, 1)
			local alpha = Health.Value / circleAlpha
			CurvedBarResize(HealthBar.TopLeft, alpha)
		end
	else
		HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
		HealthBar.TopRight.UIGradient.Transparency = NumberSequence.new(0, 0)
		HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(0, 0)
		-- update horizontal bar
	end
end

Phew, that’s all the hard part done. Now it’s time to resize the horizontal bar which will be a breeze. As mentioned earlier (and in the place I’ve provided), the bar’s size Y scale needs to be 0.12, parented inside the main frame that holds the 3 curved pieces.
Position it {0.5, 0}, {1, 0} with an anchor point of (1, 1) to line it up at the bottom center.
As for resizing the bar, we will scale it to the circleAlpha for size consistency throughout.

local circleMaxHealth = 100
local Health, MaxHealth = 200, 200

local HealthBar = script.Parent:WaitForChild("HealthBar")

local function CurvedBarResize(bar, alpha)
	bar.UIGradient.Rotation = -90 * (1 - alpha)
	if alpha - 0.001 <= 0 then
		bar.UIGradient.Transparency = NumberSequence.new(1, 1)
	elseif alpha == 1 then
		bar.UIGradient.Transparency = NumberSequence.new(0, 0)
	else
		bar.UIGradient.Transparency = NumberSequence.new({
			NumberSequenceKeypoint.new(0, 0),
			NumberSequenceKeypoint.new(alpha - 0.001, 0),
			NumberSequenceKeypoint.new(alpha, 1),
			NumberSequenceKeypoint.new(1, 1)
		})
	end
end

local function UpdateHud()
	local circleAlpha = circleMaxHealth / 3
	local topRightAlpha = circleMaxHealth * (1/3)
	local bottomRightAlpha = circleMaxHealth * (2/3)
	if Health <= circleMaxHealth then
		Health.Bar.Size = UDim2.new(0, 0, 0.12, 0)
		if Health >= bottomRightAlpha then
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.TopRight.UIGradient.Transparency = NumberSequence.new(0, 0)
			local alpha = (Health.Value - bottomRightAlpha) / circleAlpha
			CurvedBarResize(HealthBar.BottomRight, alpha)
		elseif Health >= topRightAlpha then
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(1, 1)
			local alpha = (Health.Value - topRightAlpha) / circleAlpha
			CurvedBarResize(HealthBar.TopRight, alpha)
		else
			HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
			HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(1, 1)
			local alpha = Health.Value / circleAlpha
			CurvedBarResize(HealthBar.TopLeft, alpha)
		end
	else
		HealthBar.TopLeft.UIGradient.Transparency = NumberSequence.new(0, 0)
		HealthBar.TopRight.UIGradient.Transparency = NumberSequence.new(0, 0)
		HealthBar.BottomRight.UIGradient.Transparency = NumberSequence.new(0, 0)
		HealthBar.Bar.Size = UDim2.new((Health - circleMaxHealth) / circleAlpha, 0, 0.12, 0)
	end
end

The black bar (the bar behind the health bar) will be the same concept but with MaxHealth instead. I’ll leave you to figure that out.

And there you have it! I hope this guide has helped and I hope it wasn’t too confusing. Maybe in the future we will get support for radial/coned gradients which will shrink this entire thing by 2/3.

Enjoy your new Kingdom Hearts Health bar!

116 Likes

One issue with your release is you uploaded the images to the game directly, not really accessible to others outside. Good tutorial though!

14 Likes

Yeah, people can take the image I’ve posted in the thread :slight_smile:
Edit: Fixed to use decalID instead of my assets in game!

4 Likes

Thank you so much for making a guide on this! I completely forgot about the rotation property.

5 Likes

I was so confused when you originally posted. I don’t know why it slipped my mind that it could be something other than one image xd
EDIT: Here’s what I came up with for my game :smiley:


mine is a bit choppy i need to work on it more aha

29 Likes

Love it. Somewhat followed the basic idea of this to create a radial progress bar that I plan to use in the next version of my Archimedes plugin.

\center

13 Likes

That’s epicly made! Also…

Continue reading

New version of Archimedes? :thinking::thinking: Will you add a function to select the rotation ammount and drag to the left or right to create a road rotation faster?

8 Likes

how would i keep the bar at the bottom from increasing in size based on health
also i ran into a problem while trying to do just that, from what i see the gui cant handle big numbers like 1,000,000 and wont change

Well this topic is about 2-3 years old now and this really just explains the application of UIGradient to achieve this effect. I’ll see if I can create a better worded tutorial with open-sourced working examples.

Edit: Will not be creating a new topic. Seems like there is an easy open-sourced version of this already. Consider checking this topic out! Circular/Radial Progress