local TweenService = game:GetService("TweenService")

local Module = {}

-- Function to create a new process circle
function Module.NewProcessCircle(gui: Instance, size: number?, space: number?, range: number?)
	local self = {
		Parent = gui,
		Size = size or 10,
		Space = space or 50,
		Range = range or 75,
		Fill = 0,
		CurrentFill = 0,
		Colors = {
			Fill = Color3.new(0.2, 0.8, 0.2),
			Empty = Color3.new(1, 1, 1)
		},
		Visible = true,
		Object = Instance.new("Frame", gui)
	}

	self.Object.Size = UDim2.new(0, self.Size * 2, 0, self.Size * 2)
	self.Object.BackgroundTransparency = 1
	self.Object.Position = UDim2.new(0.5, -self.Object.Size.X.Offset / 2, 0.5, -self.Object.Size.Y.Offset / 2)

	-- Initialize the dots
	for i = 0, self.Range do
		local alpha = (i / self.Range) * math.pi * 2

		local dot = Instance.new("Frame", self.Object)
		dot.Name = tostring(i)
		dot.Size = UDim2.new(0, self.Size, 0, self.Size)
		dot.Position = UDim2.new(
			0, (math.cos(alpha) * self.Space) + self.Object.Size.X.Offset / 2,
			0, (math.sin(alpha) * self.Space) + self.Object.Size.Y.Offset / 2
		)
		dot.ZIndex = -i
		local corner = Instance.new("UICorner", dot)
		corner.CornerRadius = UDim.new(1, 0)
	end

	return setmetatable(self, { __index = Module }) -- Set the metatable for method access
end

-- Method to update the visual representation of the process circle
function Module:Step()
	local percent = self.CurrentFill / 100
	local gotoIndex = math.floor(percent * self.Range)

	for i = 0, self.Range do
		local object = self.Object:FindFirstChild(tostring(i))

		if object and object:IsA("Frame") then
			object.BackgroundColor3 = (i < gotoIndex) and self.Colors.Fill or self.Colors.Empty
		end
	end
end

-- Method to update the fill value with tweening effect
function Module:Update()
	local tmpValue = Instance.new("NumberValue")
	tmpValue.Value = self.CurrentFill

	tmpValue:GetPropertyChangedSignal("Value"):Connect(function()
		self.CurrentFill = tmpValue.Value
		self:Step()
	end)

	local tweenInfo = TweenInfo.new(0.3, Enum.EasingStyle.Quart, Enum.EasingDirection.In)
	local tween = TweenService:Create(tmpValue, tweenInfo, { Value = self.Fill })
	tween:Play()
	tween.Completed:Wait()

	self.CurrentFill = self.Fill
end

-- Method to add a percentage to the fill value
function Module:Add(percentage)
	percentage = math.min(math.max(percentage, 0), 100)
	self.Fill = math.min(self.Fill + percentage, 100)
	self:Update()
end

-- Method to set the fill value directly
function Module:Set(percentage)
	percentage = math.min(math.max(percentage, 0), 100)
	self.Fill = percentage
	self:Update()
end

-- Method to set fill and empty colors
function Module:SetColors(colors)
	if colors.Fill then
		self.Colors.Fill = colors.Fill
	end
	if colors.Empty then
		self.Colors.Empty = colors.Empty
	end
	self:Step()
end

return Module

круг кривой а должен быть очень ровным исправь вот пример когда все нормально 


local module = function (self: CircularObject)
	if self.Range < 1 then return end;

	local origin = self.Parent.Position + UDim2.new(
		0, self.Parent.Size.X.Offset / 2,
		0, self.Parent.Size.Y.Offset / 2
	)

	for i = 0, self.Range do
		local aplha = (i / self.Range) * math.pi * 2

		local dot = Instance.new("Frame", self.Object)
		dot.Name = tostring(i)
		dot.Size = UDim2.new(0, self.Size, 0, self.Size)
		dot.Position = UDim2.new(
			0, (math.cos(aplha) * self.Space) + origin.X.Offset,
			0, (math.sin(aplha) * self.Space) + origin.Y.Offset
		)
		dot.ZIndex = -i
		local corner = Instance.new("UICorner", dot)
		corner.CornerRadius = UDim.new(1, 0)

	end
end

return module