Setting the direction of a Tween

  1. What do you want to achieve?
    Set the rotation direction of Tween. When a button is pressed, gears rotate. Green rotates 90, blue rotates -90, and red rotates -180.

  2. What is the issue?
    When rotating by -180, it only rotates one direction, ridding of the “gear” effect.
    Here is an image illustrating my goal and comparing it to what happens.

  3. What solutions have you tried so far?
    I could try making two tweens that would run consecutively, but with my current script it would be inconvenient.
    I read up some information on tweens and understand that Tweens move the shortest distance. However, since my parts are moving in the 180 direction, both 180 and -180 should theoretically be the same distance.

The current code is contained in a ModuleScript. When a button is pressed, it activates the module script which inputs information about the button. Each gear has a Configuration object which says what direction the gear should move. Only gears contained in the same room as the button, which is indicated by a tag.

Here is my code:

local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local CollectionService = game:GetService("CollectionService")
local DebrisService = game:GetService("Debris")

local GearScript = {}
GearScript.__index = GearScript

function GearScript.new(Button)
	local RotationAmount
	if Button.Configuration.ButtonColor.Value == "Green" then
		RotationAmount = 90
	elseif  Button.Configuration.ButtonColor.Value == "Blue" then
		RotationAmount =  -90
	elseif Button.Configuration.ButtonColor.Value == "Red" then
		RotationAmount =  -180
	end
	local RoomNumber = Button.Configuration.RoomNumber.Value
	RotateAllGears(RoomNumber, RotationAmount)
	wait(5)
end

function Rotate(Center, Amount, RotationAxis)
-- RotationDirection is 1 or -1
	local RotationDirection = Center.Configuration.RotationDirection.Value

	local info = TweenInfo.new(1,Enum.EasingStyle.Sine,Enum.EasingDirection.InOut,0,false,0)

	local goals = {
		CFrame = Center.CFrame * CFrame.fromAxisAngle(RotationAxis, math.rad(Amount)*RotationDirection)
		}

	local tween = TweenService:Create(Center, info, goals)

	tween.Parent = Center
	tween:Play()
--Add the tween to DebrisService
	DebrisService:AddItem(tween,2)
end

function RotateAllGears(RoomNumber, RotationAmount)
	--Goal: Rotate all gears within a room.
	--Find all gears in the world, and see which are in the same RoomNumber as the button.
	for _,part in pairs(CollectionService:GetTagged("Gears")) do
		if part.Configuration.RoomNumber.Value == RoomNumber then
			if not part:FindFirstChild("Tween") then
				if part.Configuration.GearType.Value == "Horizontal" then
					Rotate(part, RotationAmount, Vector3.new(0,1,0))
				elseif part.Configuration.GearType.Value == "Vertical" then
					Rotate(part, RotationAmount, Vector3.new(1,0,0))
				end
			end
		end
	end
end

return GearScript

Your help is very much appreciated. Thank you. :heart:

Bad news, that kind of rotation is the weak point of Tweenservice, (although I think there was once an update that fixed it, I’m not really sure)

Anyway, to have more control over our animations it’s better to do it manually, for example

for i = 0, 1, 1/30 do
	Center.CFrame = cf0 * CFrame.fromAxisAngle(RotationAxis, i*delta)
	task.wait(1/30)
end

With a small change you can make it have the same smoothness as Tweenservice (Enum.EasingStyle.Sine).

function Rotate(Center, Amount, RotationAxis)
	-- RotationDirection is 1 or -1
	local RotationDirection = Center.Configuration.RotationDirection.Value

	local cf0 = Center.CFrame
	local delta = math.rad(Amount)*RotationDirection
	task.defer(function()
		for i = 0, 1, 1/30 do
			Center.CFrame = cf0 * CFrame.fromAxisAngle(RotationAxis, math.sin(i*math.pi/2)*delta)
			task.wait(1/30)
		end
	end)
end
1 Like

Thanks. This is a good step in the right direction, and I never seen task.defer before! So, thank you for introducing it to me.

I think this could work, but my concern is that it appears slower. Is this because of the task.wait(1/60)? Here’s a video. Left is using Tween, Right is using the new function.
robloxapp-20240104-1736188.wmv (138.2 KB)

How can I specify the time using this for-loop method?

The following is the code I have so far. Some notes:

  1. I looked up the function for Enum.EasingStyle.Sine and Enum.EasingDirection.InOut, and it is 0.5*(1+math.sin(math.pi*(i+3/2))). I changed it accordingly in case you are wondering.

  2. I added the variables DeltaTime and TotalTime, but these could be the reason why it appears slower. Here’s my code:

function Rotate(Center, Amount, RotationAxis)
	-- RotationDirection is 1 or -1
	local RotationDirection = Center.Configuration.RotationDirection.Value

	local cf0 = Center.CFrame
	local DeltaTheta= math.rad(Amount)*RotationDirection

	local DeltaTime = 1/60
	local TotalTime = 1

	task.defer(function()
		for i = 0, TotalTime, DeltaTime do
			Center.CFrame = CFrame0 * CFrame.fromAxisAngle(RotationAxis, 0.5*(1+math.sin(math.pi*(i+3/2)))*DeltaTheta)
			task.wait(DeltaTime)
		end
	end)
end

Hi, update. I was messing around with the task.defer() structure and I think I found the issue. It seems the task.wait(DeltaTime) waits twice as long than it should. I changed it to task.wait(DeltaTime/2) and it appeared to match. I’m pretty confused, is this expected?

Also, I noticed that the video file I put did not upload correctly so here is an mp4. Left uses TweenService and right uses the code suggested.