Radial Selection UI not working properly - suggestions?

I’m currently working on a project which involves a radial selection UI. The general idea is to have a grey part that spins in order to show the current selection. I managed to make this work, however once I cross from the negative direction into the positive direction (past 360deg back to 0deg), the grey part moves backwards around the circle instead of directly to the next selection.

I realise this is an issue with how I’m animating the UI, however I’m not sure what I can do to work around this and get my intended effect without compromising on the animations. I just need the grey selection bit to rotate from grab to pin (and vice versa) without it taking a trip around the entire circle.

The snippet of code I’m using is below. I currently calculate the angle between 2 vectors in order to get the rotation I need for the spinning part of the UI and make it snap to the next selection once the angles pass certain bounds.

UserInputService.InputChanged:Connect(function(input, gameProcessed)
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		local Mouse = UserInputService:GetMouseLocation()
		local CentreVector = Vector2.new(Camera.ViewportSize.X/2, Camera.ViewportSize.Y/2)
		local RelativeMousePosition = Mouse - CentreVector

		local UnitVector = Vector2.new(0, -1)
		local Dot = UnitVector:Angle(RelativeMousePosition, true)
		local Angle = math.deg(Dot)

		if Angle >= 0 and Angle < 90 then
			spr.target(Container.Radial.SpinContainer, 1, 4, { Rotation = 0 })
		elseif Angle >= 90  and Angle <= 180 then
			spr.target(Container.Radial.SpinContainer, 1, 4, { Rotation = 90 })
		elseif Angle <= 0 and Angle < -90 then
			spr.target(Container.Radial.SpinContainer, 1, 4, { Rotation = 180 })
		else
			spr.target(Container.Radial.SpinContainer, 1, 4, { Rotation = 270 })
		end
		
		--Container.Radial.SpinContainer.Rotation = math.deg(Dot) - 45
	end
end)

Example of what’s happening:

Does anyone have idea on how I can work around this without compromising on the animations?

I see the issue. You need to make it so that when going through:

Top Right (assuming 270°) → Top Left (assuming 0°): tween 270° → 360° (then immediately after finishing, the value goes to 0° [not tweening])

Top Left (assuming 0°) → Top Right (assuming 270°): before playing tween/anim, set value to 360°(without tween), then tween 360° → 270°

I also made a weapon wheel, heres some code from it.

local function shortestAngle(current: number, target: number)
	local difference = (target - current + 180) % 360 - 180
	return current + difference
end

Pretty sure its self explanatory but just tween the angle to shortestAngle(frame.Rotation, TargetRotation)

1 Like

Thank you. Had to do a few quirky bits for the sonde_v’s solution to make it work, but your solution is the most simple and cleanest one.

If you don’t mind, could you possibly explain what the maths behind it does?

1 Like

spent a bit typing out an explanation but i think chat gpt is a better teacher so

The formula (target - current + 180) % 360 - 180 adapts to this by:

  1. Shift the difference to positive space: (target - current + 180) ensures the difference doesn’t immediately fall into a negative range.
  2. Wrap the result within 360 degrees: % 360 wraps the value into a circle, keeping it in the valid range [0, 360).
  3. Adjust for shortest path: Subtracting 180 shifts the result into [-180, 180], the standard range for Roblox’s angular operations where rotation directions are intuitive:
    • Positive values mean clockwise rotation.
    • Negative values mean counterclockwise rotation.
1 Like

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