Placing gui buttons along the radius of a ring decal

hi, I wanted to do something new with guis that I have not done before but im not sure how I would go about doing it.

below I have a gui consisting of a button and a ring decal. I want to use that 1 button and clone it along the radius of the ring, I would also like to make sure all buttons cloned are evenly spaced from themselves.

the trouble is how I would script this. Ive never applied CFrames to gui elements before and if it was as easy as doing that then this would be no problem I would just have a CFrame point at a position at an offset and rotate it in a loop.

were would I begin with this?

1 Like

You don’t use CFrames for UI. You use Vector2s, UDim2s, and simple trigonometry.

First, to evenly distribute the buttons so that there is an equal amount of space between each of them, you just divide 360 by the number of buttons. That’s going to be the angle increment.

local buttonsAmount: number = 8

local angleIncrement: number = 360/buttonsAmount
for i = 0, buttonsAmount-1 do --start at zero so the first button will be on 0 degrees
	local angle: number = math.rad(angleIncrement*i)
	...
end

Next, we need to use the unit circle. If the radius was just one, the unit circle tells us that the position is just Vector2.new(math.cos(θ), math.sin(θ)). This vector conveniently will always have a length of one so we can use it as the direction to get positions that are further away from the circle. Then we just need to multiply the radius to the direction to get the final position of the button.

for i = 0, buttonsAmount-1 do
	local angle: number = math.rad(angleIncrement*i)
	local x, y = math.cos(angle), math.sin(angle)
	local position: Vector2 = Vector2.new(x, y)*radius
	...
end

But to actually use it you need to do a lot more. The position we have right now is the displacement from the center of the circle. Some more math is involved to turn that Vector2 into an actually useable UDim2 position, but I’ll leave that in the demo file below for you to goof around in:

radialMenu.rbxl (49.8 KB)

Code inside the demo
local buttonsAmount: number = 9 --number of buttons to add
local angleOffset: number = -90 --makes the first button start at the top center of the circle

local sui = script.Parent
local buttonEx = script:WaitForChild('TextButton')
local main = sui:WaitForChild('CanvasGroup')
local frame = main:WaitForChild('Frame')
local stroke = frame:WaitForChild('UIStroke')

local absoluteRadius: number = stroke.Thickness*.5
local angleIncrement: number = 360/buttonsAmount

for i = 0, buttonsAmount-1 do
	local angle: number = math.rad(angleIncrement*i + angleOffset)
	local x, y: number = math.cos(angle), math.sin(angle)
	--currently, the direction is in the interval [-1, 1] for both axes.
	--we need to convert it into the interval [0, 1] for the UDim to work properly.
	local x01, y01: number = (x+1)*.5, (y+1)*.5
	--the position will use both scale and offset!
	--scale is [0, 1] so we don't need to do anything. offset however, will depend on the thickness of the circle.
	--the offset is to move the button into the actual circle part of the frame, since the circle is just a stroke outline.
	local position: UDim2 = UDim2.new(x01, x*absoluteRadius, y01, y*absoluteRadius)
	
	local new = buttonEx:Clone()
	new.Position = position
	new.Parent = frame
end

4 Likes

thank you for this in depth response, complex math is probably my weakest skill throughout my time as a developer but with a bit of studying and playing around this is gonna help a lot for what I am doing now and anything more in the future.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.