Circular UI Module for Roblox - Easily Arrange UI Elements in a Circle

,

Hey everyone,

I came across a challenge while working on my UI, where I wanted to position frames in a perfect circle around a specific parent frame. After a bit of tinkering, I created a module that automates this process. This means arranging UI instances in a circular formation with a simple call, no by-hand math or positioning needed.

Before you continue

This module only works completely properly with scale, not offset, this is due to personal reasons, you may click off now if this does not suit your needs.

How It Works:

To use this module, just place the code inside a ModuleScript:

local CircularUI = {}

-- List of valid UI instances that can be arranged in a circle
local validUIInstances = {
	"Frame",
	"ImageButton",
	"ImageLabel",
	"ScrollingFrame",
	"TextBox",
	"TextLabel",
	"TextButton",
	"VideoFrame",
	"ViewportFrame"
}

local default = "Frame" -- Default instance type if not specified
local defaultInterval = 0.1 -- Default interval for auto-updates

-- Helper function to check if an instance is valid for circular arrangement
local function isValidInstance(instance, instanceType)
	if instanceType == "all" then
		return table.find(validUIInstances, instance.ClassName) ~= nil
	else
		return instance:IsA(instanceType)
	end
end

-- Function to position all valid instances in a circle around the parent frame
function CircularUI.updateFramesInCircle(parentFrame, instanceType)
	local instances = parentFrame:GetChildren()
	local frameCount = 0

	-- Count the valid instances
	for _, instance in pairs(instances) do
		if isValidInstance(instance, instanceType) then
			frameCount = frameCount + 1
		end
	end

	if frameCount == 0 then return end

	-- Control the circle's radius with a parent frame attribute
	local circleRadiusScale = parentFrame:GetAttribute("circleRadiusScale") or 1.25

	local angleStep = 360 / frameCount -- Divide 360 degrees by the number of instances
	local centerX, centerY = 0.5, 0.5 -- Center point of the circle

	-- Arrange the valid instances in a circle
	local instanceIndex = 0
	for _, instance in pairs(instances) do
		if isValidInstance(instance, instanceType) then
			local angle = math.rad(instanceIndex * angleStep)
			local xPos = centerX + (circleRadiusScale * math.cos(angle))
			local yPos = centerY + (circleRadiusScale * math.sin(angle))

			instance.Position = UDim2.new(xPos - (instance.Size.X.Scale / 2), 0, yPos - (instance.Size.Y.Scale / 2), 0)
			instanceIndex = instanceIndex + 1
		end
	end
end

-- Function to start auto-updating the circular layout at regular intervals
function CircularUI.startAutoUpdate(parentFrame, interval, instanceType)
	interval = interval or defaultInterval
	instanceType = instanceType or default

	-- Continuously update the layout
	while task.wait(interval) do
		CircularUI.updateFramesInCircle(parentFrame, instanceType)
	end
end

return CircularUI

Usage

In a LocalScript, you can use the module like this:

local CircularUI = require(path.to.module.here)
local parentFrame = script.Parent

-- Automatic update looping:
CircularUI.startAutoUpdate(parentFrame, 0.1, "all")
-- CircularUI.startAutoUpdate(parentFrame <Instance>, interval <Number>, instanceType <String>)

-- One-time update:
CircularUI.updateFramesInCircle(parentFrame, "ImageLabel")
-- CircularUI.updateFramesInCircle(parentFrame <Instance>, instanceType <String>)

Features

  • Auto-Update: This feature automatically adjusts the layout whenever new instances are added to the parent frame. The interval for updates is controlled by the user, so you can keep the frames dynamically arranged in real-time.
  • Radius Scale: The radius of the circle can be adjusted by setting the circleRadiusScale attribute on the parent frame. By default, it’s set to 1.25, but you can customize it to make the circle larger or smaller depending on your UI layout.
  • Flexible Instance Filtering: You can choose to arrange specific types of UI elements, such as ImageLabel or Frame, or simply set the instanceType to "all" to include any valid UI element in your layout. Here’s a list of supported types:
    • Frame
    • ImageButton
    • ImageLabel
    • ScrollingFrame
    • TextBox
    • TextLabel
    • TextButton
    • VideoFrame
    • ViewportFrame

Conclusion

This module is a simple yet effective way to dynamically position UI elements in a circle around a parent instance. Whether you need to organize frames, image labels, or any other valid UI instance, this module can help you create that circular arrangement with ease.

Feel free to tweak it, add features, or use it however you like!

Happy developing! :tada:


Disclaimer

At the moment this module struggles to handle positioning of objects with their AnchorPoints set, I plan to fix this in the future, but for the time being this is more of a learning resource than an actual development resource. If you end up fixing any of the problems or improve upon this resource in any way, you may contribute by leaving the modified code below, I will test the code and update this forums post dependant on whether or not your code fixes the problems specified.

Examples


image

All these are done while the game was running and I called the auto-update function on the parent frame, and passed through “all” as the instance type.

3 Likes

Hey, can you add some examples?

1 Like

Just like the person above has stated, examples please! Thank you.

1 Like

Looks very cool, I’ll look into using this.

Is this similar to how Counter Blox did to their buying UI?
Can we make a curve type UI element as can be seen in counter blox buying menu?