How do I smooth this out?

Hello.
When testing this script, when the swingPart is slowing, it snaps to the initialCFrame near the end. I have tried and tried but cannot fix any of it and am really confused. If anyone could help me, it would be greatly apricated.

local RunService = game:GetService("RunService")

local swingPart = workspace.RideSystem.Parts.SwingArm.MovePart

local maxAmplitude = 120  				-- Max swing amplitude (degrees)
local initialSwingSpeed = 0.75 			-- Speed for oscillation
local holdTime = 15 					-- Time to swing at the peak (seconds)
local totalTime = 45 					-- Total time for the full cycle (seconds)
local resetDuration = 2 				-- Time to smoothly reset back to the initial position

local eTime = 0 						-- Elapsed time
local swingAmplitude = 0 				-- Current amplitude
local isSwinging = true 				-- Flag to indicate if the swing is active
local isResetting = false 				-- Flag to indicate if the swing is resetting

local function lerp(a, b, t)
	return a + (b - a) * t
end

local initialCFrame = swingPart.CFrame

RunService.Heartbeat:Connect(function(deltaTime)
	if isSwinging then
		eTime += deltaTime

		if eTime < totalTime then
			if eTime < (totalTime / 3) then
				local progress = eTime / (totalTime / 3)
				swingAmplitude = lerp(10, maxAmplitude, progress) 								-- Amplitude increases to max amplitude
			elseif eTime < (totalTime / 3) + holdTime then
				swingAmplitude = maxAmplitude 													-- Swinngs at max amplitude
			else
				local progress = (eTime - (totalTime / 3) - holdTime) / (totalTime / 3)
				swingAmplitude = lerp(maxAmplitude, 0, progress)								-- Amplitude decreases to 0
			end

			local swingAngle = math.sin(eTime * initialSwingSpeed) * math.rad(swingAmplitude)
			swingPart.CFrame = initialCFrame * CFrame.Angles(swingAngle, 0, 0)
		else
			isSwinging = false
			isResetting = true
			eTime = 0
		end
	elseif isResetting then
		local progress = eTime / resetDuration
		swingPart.CFrame = swingPart.CFrame:Lerp(initialCFrame, progress)

		eTime += deltaTime
		if eTime >= resetDuration then
			swingPart.CFrame = initialCFrame
			isResetting = false
		end
	end
end)```
1 Like

Try printing just the variables used each time in the swingPart.CFrame calculation.

Something like:

        print("eTime = ", eTime, "   initialSwingSpeed = ",  initialSwingSpeed, "   swingAmplitude = ", swingAmplitude)
		local swingAngle = math.sin(eTime * initialSwingSpeed) * math.rad(swingAmplitude)
		swingPart.CFrame = initialCFrame * CFrame.Angles(swingAngle, 0, 0)

I can’t be sure because the calculations hurt my brain :thinking: but it appears to me there’s an issue with the final eTime number that’s causing this, possibly with the lerp of the swingAmplitude.

2 Likes

They are starting to hurt my brain too :joy:

I have printed them out and the last two prints are not like the rest. Normally the swingAmpltude between prints are very very small, like 0.05 small. The last two has a jump of around 0.15

Just before last print:
eTime = 44.977358057396486
initialSwingSpeed = 0.75
swingAmplitude = 0.1811355408281088

Last print out:
eTime = 44.9953412276227
initialSwingSpeed = 0.75
swingAmplitude = 0.03727017901837826

Not sure if this will help you, but hopefully it does :slight_smile:

So have a look at swingAmplitude since it’s 1/3rd the value and the difference seems to be what’s causing the issue when you multiply by .181 or .037.

Try printing the variables before this code calculation to see where the 3x difference is. If it doesn’t give you any clues keep tracking back to the previous calculation.

		local progress = (eTime - (totalTime / 3) - holdTime) / (totalTime / 3)
		swingAmplitude = lerp(maxAmplitude, 0, progress)	
2 Likes

Okay so I have done some testing and managed to get it so it runs another lerp for slow and steady movements at the end. I am not sure if its because my brain is fried or what but I am running into the issue now that the rotating swing part resets to its original CFrame and then runs them tweens. I am guessing this is because the first main motion is returning it to the original CFrame, but I have no idea how to stop that.

If you have any ideas, it would be an amazing help :slight_smile:

local RunService = game:GetService("RunService")
local swingPart = workspace.RideSystem.Parts.SwingArm.MovePart

local maxAmplitude = 120  -- Max swing amplitude (degrees)
local initialSwingSpeed = 0.75  -- Speed for oscillation
local holdTime = 15  -- Time to swing at the peak (seconds)
local totalTime = 45  -- Total time for the full cycle (seconds)
local resetDuration = 5  -- Time to smoothly reset back to the initial position

local eTime = 0  -- Elapsed time
local swingAmplitude = 0  -- Current amplitude
local isSwinging = true  -- Flag to indicate if the swing is active
local isResetting = false  -- Flag to indicate if the swing is resetting

local function lerp(a, b, t)
	return a + (b - a) * t
end

local function easeOutSine(t)
	return math.sin((t * math.pi) / 2)
end

local initialCFrame = swingPart.CFrame

RunService.Heartbeat:Connect(function(deltaTime)
	if isSwinging then
		eTime += deltaTime

		if eTime < totalTime then
			if eTime < (totalTime / 3) then
				local progress = eTime / (totalTime / 3)
				swingAmplitude = lerp(10, maxAmplitude, progress)  -- Amplitude increases to max amplitude
			elseif eTime < (totalTime / 3) + holdTime then
				swingAmplitude = maxAmplitude  -- Swings at max amplitude
			else
				local progress = (eTime - (totalTime / 3) - holdTime) / (totalTime / 3)
				swingAmplitude = lerp(maxAmplitude, 0, progress)  -- Amplitude decreases to 0
			end

			local swingAngle = math.sin(eTime * initialSwingSpeed) * math.rad(swingAmplitude)
			swingPart.CFrame = initialCFrame * CFrame.Angles(swingAngle, 0, 0)
		else
			isSwinging = false
			isResetting = true
			eTime = 0
		end
	elseif isResetting then
		eTime += deltaTime
		local progress = eTime / resetDuration
		local easedProgress = easeOutSine(progress)
		local swingAngle = math.sin(eTime * initialSwingSpeed) * math.rad(5 * (1 - easedProgress))

		local reversedSwingAngle = swingAngle * -1
		local targetCFrame = initialCFrame * CFrame.Angles(reversedSwingAngle, 0, 0)
		swingPart.CFrame = swingPart.CFrame:Lerp(targetCFrame, easedProgress)

		if eTime >= resetDuration then
			swingPart.CFrame = initialCFrame
			isResetting = false
		end
	end
end)

Is this because these if checks aren’t giving you the results you’d expect? Try:

            -- print eTime, totalTime, and holdTime
			elseif eTime < (totalTime / 3) + holdTime then

or

        -- print eTime and resetDuration
		if eTime >= resetDuration then 

To see if you are getting a check doing something like comparing 3 to 2.99999 which is failing the if check.