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 to1.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
orFrame
, 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!
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
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.