Issue with tweening gui rotation

I’m currently making a radial selection menu, however I’m trying to tween the line that points towards what you’re selecting only issue is I’m having an issue with tween service and it snapping around.

F5gRA6eds5

If you have any ways I can negate this effect or remove it entirely then please inform me.
Thanks!

Here’s the code I’m using for this:

--vars

local Area = script.Parent.Area
local Center = Area.Center
local PointLine = Center.PointLine
local Buttons = Area.Buttons

local players = game:GetService("Players")
local localPlayer = players.LocalPlayer
local UIS = game:GetService("UserInputService")
local REPLICATED_STORAGE = game:GetService("ReplicatedStorage")
local TWEEN_SERVICE = game:GetService("TweenService")
local HTTP_SERVICE = game:GetService("HttpService")

local open = false
local lastRotTween = false

-- events

local INVENTORY_EVENT_FOLDER = REPLICATED_STORAGE:WaitForChild("Inventory")
local EVENTS = {
	["updateInventory"] = INVENTORY_EVENT_FOLDER:WaitForChild("updateInventory"),
	["serverCheck"] = INVENTORY_EVENT_FOLDER:WaitForChild("serverCheck"),
	["clientCheck"] = INVENTORY_EVENT_FOLDER:WaitForChild("clientCheck"),
}

-- modules

local INVENTORY_MODULE = require(script:WaitForChild("Inventory"))

-- functions

-- base functions 
do
	baseFunctions = {}
	function baseFunctions.GetAngle(v1,v2)
		local dif = (v1-v2)
		local r = math.atan2(dif.Y,dif.X) --* (180 / math.pi)
		return  math.deg(r)
	end

	function baseFunctions.UpdatePointLine()
		local mp = UIS:GetMouseLocation()
		local angle = baseFunctions.GetAngle(mp,Center.AbsolutePosition)
		TWEEN_SERVICE:Create(PointLine,TweenInfo.new(0.15,Enum.EasingStyle.Linear),{Rotation = angle}):Play()
	end
end

-- main functions
do
	mainFunctions = {}
	function mainFunctions.setupServerConnection()
		EVENTS.serverCheck.OnClientEvent:Connect(function(key)
			EVENTS.serverCheck:FireServer({key,INVENTORY_MODULE})
		end)
	end

	function mainFunctions.requestPlayerInventory()
		local key = HTTP_SERVICE:GenerateGUID(false)
		local ans = EVENTS.updateInventory:InvokeServer(key)
		repeat wait() until ans 
		
		if ans["key"] == key then
			INVENTORY_MODULE["Inventory"] = ans["newInv"]
		end
	end
	
	function mainFunctions.collidesWith(gui1, gui2) ---A little different wording but it serves the same purpose
		local gui1_topLeft = gui1.AbsolutePosition
		local gui1_bottomRight = gui1_topLeft + gui1.AbsoluteSize

		local gui2_topLeft = gui2.AbsolutePosition
		local gui2_bottomRight = gui2_topLeft + gui2.AbsoluteSize

		return ((gui1_topLeft.x < gui2_bottomRight.x and gui1_bottomRight.x > gui2_topLeft.x) and (gui1_topLeft.y < gui2_bottomRight.y and gui1_bottomRight.y > gui2_topLeft.y))
	end
end



local module = {}

function module.init()
	task.spawn(mainFunctions.setupServerConnection())
	mainFunctions.requestPlayerInventory()
end

function module.callServerCheck()
	
end

function module.runUpdates()
	repeat
		baseFunctions.UpdatePointLine()
		for _,v in pairs(Area.Buttons:GetChildren()) do
			if mainFunctions.collidesWith(PointLine.Tip,v) then
				TWEEN_SERVICE:Create(v,TweenInfo.new(0.15,Enum.EasingStyle.Linear),{Size = UDim2.new(0.172, 20,0.172, 20)}):Play()
			else
				TWEEN_SERVICE:Create(v,TweenInfo.new(0.15,Enum.EasingStyle.Linear),{Size = UDim2.new(0.172, 0,0.172, 0)}):Play()
			end
		end
		wait()
	until open == false
end

function module.toggleInventory(v)
	if v == false then
		UIS.MouseIconEnabled = true
		open = false
		Area:TweenSizeAndPosition(UDim2.new(0,0,0,0),UDim2.new(0.5,0,2,0),Enum.EasingDirection.InOut,Enum.EasingStyle.Linear,0.5)
	elseif v == true then
		--UIS.MouseIconEnabled = false
		open = true
		task.spawn(function()
			module.runUpdates()
		end)
		Area:TweenSizeAndPosition(UDim2.new(0.252, 0,0.499, 0),UDim2.new(0.5,0,0.5,0),Enum.EasingDirection.InOut,Enum.EasingStyle.Linear,0.5)
	end
	
	
end

return module

1 Like

Because you’re running it every frame you wouldn’t need TweenService. Have you tried doing it without TweenService? AbsolutePosition also doesn’t account for AnchorPoint so you’d have to watch out for that if you’re using an anchor point other than 0,0. It works when I test it out.

I’m using tween service to smooth the movement, and if your talking about the collision thing that uses a whole different gui object

I’ve made a system that works, try using this as a reference:

Pointer.rbxm (4.6 KB)
(drag and drop the file to studio)



TweenService will make it “smoother” in a sense, but I feel like using Springs would fit your use case more.

local newSpring = Spring.new(currentRotation)
newSpring.Speed = desiredSpeed -- This is how fast it will animate

repeat
    newSpring.Target = newRotation < 0 and 360 + newRotation or newRotation
    Frame.Rotation = newSpring.Value
    wait()
until open == false

Here’s a version with springs:
PointerWithSprings.rbxm (6.5 KB)

Keep in mind that when using springs you’d have to account for a number jumping from 360 to 0 as that will cause problems.

Sorry for the late reply, I just tested this the spring works a lot better than the tweening, however I am having an issue with it snapping from 0,360 I tried to use the code snippet you sent but that didn’t fix it.

Yeah, I noted that at the end of my reply. After some trial and error, I got it to work with this code:

local UIS = game:GetService("UserInputService")
local Pointer = script.Parent.Pointer
local Center = script.Parent.Center
local Spring = require(script.Spring)

local newSpring = Spring.new(0) -- Creation a spring with a value of 0
newSpring.Speed = 10 -- Spring speed (higher means faster)

while wait() do
	local a = Center.AbsolutePosition + Vector2.new(3, 3) -- Don't worry about the addition, it's to solve the anchor point problem
	local b = UIS:GetMouseLocation() - Vector2.new(0, 36) -- GetMouseLocation ignores gui inset
	local rot = math.deg(math.atan2(a.y - b.Y, a.X - b.X)) -- Getting the rotation

	if rot < 0 then
		--[[
			Rotation from atan2 ranges from -180 to 180. This
			just makes sure that rotation ranges from 0 - 360
			to make things less complicated
		]] 
		rot += 360
	end
	if math.abs(newSpring.Value - rot) >= 180 then -- When the spinner goes from 360 past 0
		if newSpring.Value > 180 and rot < 180 then -- When the spinner goes past 360 degrees
			newSpring.Value = -360 + newSpring.Value
		else -- When the spinner goes below 0 degrees
			newSpring.Value = 360 + newSpring.Value
		end
	end
	newSpring.Target = rot
	Pointer.Rotation = newSpring.Value
end

The code basically checks if the rotation goes over 360 or under 0. If it does, it sets the value of the spring to a congruent angle that’s ranges from 0 - 360. For example, an angle of 370 degrees would be converted to 10 degrees [-360 + 370 = 10], and an angle of -50 degrees would be converted to 310 degrees [360 + (-50) = 310].

There might be a better way to detect it going over 360 and under 0, but this is the result:


FixedPointerWithSprings.rbxm (6.8 KB)

2 Likes

Thank you for this, I don’t know how I could of managed this without your help, have a nice day/night!

1 Like