Rotation Issues?

I currently am having issues with my gui as of right now. I want it to have a lerping effect so it lags behind the mouse to make it look better but its having some weird properties when i move past a certain point where it’ll bug out before fixing itself. I’ve tried looking for solutions on other forums but couldn’t find it.

Here’s my current code

Connections.RadialRotationConnection = RunService.Heartbeat:Connect(function(DeltaTime)
		local RotationHolder = EmoteUI.Frame.RotationHolder
		
		local MousePos = UserInputService:GetMouseLocation()
		local Center = RotationHolder.AbsolutePosition + (RotationHolder.AbsoluteSize * .5)
		local Disp = (MousePos - Center)
		local Angle = math.deg(math.atan2(Disp.Y - 46, Disp.X))
		local FinalAngle = Angle + 90
		
		local CurAngle = FunctionUtils:Lerp(RotationHolder.Rotation, FinalAngle, 0.25)
		
		RotationHolder.Rotation = CurAngle
	end)

If anyone can help me that would be appreciated

1 Like

I forgot to include videos on the issue so here

This does work Edit: forgot about lerp. This does work with the lag effect

--!strict

local run_service = game:GetService("RunService")
local gui_service = game:GetService("GuiService")
local tween_service = game:GetService("TweenService")

local arrow = script.Parent.arrow
local center = script.Parent.center
assert(game.Players.LocalPlayer)
local mouse = game.Players.LocalPlayer:GetMouse()

local function update_rotation()
	local mouse_pos = Vector2.new(mouse.X, mouse.Y)
	
	local arrow_center = arrow.AbsolutePosition + arrow.AbsoluteSize / 2
	
	local inset, _ = gui_service:GetGuiInset()
	
	center.Position = UDim2.fromOffset(arrow_center.X + inset.X, arrow_center.Y + inset.Y)
	
	local direction = (mouse_pos - arrow_center)
	local angle = math.atan2(direction.X, direction.Y)
	
	local new_angle = -math.deg(angle) + 90
	if math.abs(arrow.Rotation - new_angle) > 180 then
		new_angle += 360
	end
	arrow.Rotation = tween_service:SmoothDamp(arrow.Rotation, new_angle, 0, .05)
end
run_service.PreRender:Connect(update_rotation)

arrow.rbxm (7.0 KB)

This is happening because your lerp function is trying to interpolate from A to B, where A and B do not connect. But when lerping angles, A and B do connect.

Imagine your angle is at 350 degrees, and the next target angle is 10 degrees. The ideal interpolation would rotate +20 degrees. However, a normal lerp will interpolate -340 degrees instead. It has no concept that it can lerp forward to get to 10 degrees from 350.

Thankfully, we can write a lerp function that properly deals with angles. It properly wraps the value around the 360 degree mark. This can be directly swapped out for your FunctionUtils:Lerp function:

local function LerpAngle(from: number, to: number, alpha: number): number
	local diff = math.fmod(to - from, 360)
	diff = math.fmod(diff * 2, 360) - diff
	return from + diff * alpha
end

-- ...
local CurAngle = LerpAngle(RotationHolder.Rotation, FinalAngle, 0.25)

A couple notes:

  • To make your code non FPS-dependent, see Freya Holmer’s post about lerping with delta time correctly.
  • The LerpAngle function above assumes degrees. For radians, swap out the two instances of 360 with math.pi * 2.

Here’s an implementation of Holmer’s framerate-independent method along with the above LerpAngle function:

local function SmoothLerpAngle(from: number, to: number, dt: number, h: number): number
	return LerpAngle(from, to, 1 - math.exp(-dt / h))
end

-- ...
-- 0.2 is the "halflife" (smaller value is quicker movement)
local CurAngle = SmoothLerpAngle(RotationHolder.Rotation, FinalAngle, DeltaTime, 0.2)