Cutscene not transitioning smoothly

I’m currently trying to make a cutscene module for my game which will have lots of functions for all the advanced cutscenes I plan to make.

I have currently run into a problem where even though the transition between the cframes in the table are smoothed using Bezier curves, it still isn’t smooth when going from another section in the table with control points, and the turn is easily noticeable. It just looks bad.

I have tried changing the positions of each cframe, and changing the way they are interpolated but it doesn’t seem to work. I don’t know what to do.

Here is the entire script:

local CutsceneModule = {}

-- Variables --
local RunService = game:GetService("RunService")
local Lighting = game.Lighting
local Camera = workspace.CurrentCamera

local LocalPlayer = game.Players.LocalPlayer
local Character = LocalPlayer.CharacterAdded:Wait()
Character.Archivable = true

-- Helper Functions --

local function createCFrame(position, rotation)
	return CFrame.new(position) * CFrame.Angles(math.rad(rotation.X), math.rad(rotation.Y), math.rad(rotation.Z))
end

-- Cubic Bezier formula
local function bezierLerp(t, p0, p1, p2, p3)
	local u = 1 - t
	local tt = t * t
	local uu = u * u
	local uuu = uu * u
	local ttt = tt * t

	local point = (uuu * p0) + (3 * uu * t * p1) + (3 * u * tt * p2) + (ttt * p3)
	return point
end

local function playTweenSequence(camera, tweenData)
	local totalDuration = 0
	
	for i = 1, #tweenData, 4 do
		totalDuration = totalDuration + tweenData[i].duration
	end

	local currentTime = 0

	local function getInterpolatedCFrame(t)
		local cumulativeTime = 0

		for i = 1, #tweenData, 4 do
			local segment = tweenData[i]
			local controlPoint1 = tweenData[i + 1]
			local controlPoint2 = tweenData[i + 2]
			local endCFrame = tweenData[i + 3]

			cumulativeTime = cumulativeTime + segment.duration

			if t * totalDuration <= cumulativeTime then
				local segmentStartTime = cumulativeTime - segment.duration
				local localTime = (t * totalDuration - segmentStartTime) / segment.duration

				local newPosition = bezierLerp(
					localTime,
					segment.startCFrame.Position,
					controlPoint1,
					controlPoint2,
					endCFrame.Position
				)
				
				local startLookVector = segment.startCFrame.Position + segment.startCFrame.LookVector
				local endLookVector = endCFrame.Position + endCFrame.LookVector

				local newLookAtPosition = bezierLerp(
					localTime,
					startLookVector,
					controlPoint1 + segment.startCFrame.LookVector,
					controlPoint2 + endCFrame.LookVector,
					endLookVector
				)

				return CFrame.new(newPosition, newLookAtPosition)
			end
		end
	end

	RunService.RenderStepped:Connect(function(deltaTime)
		currentTime = currentTime + deltaTime
		local alpha = math.clamp(currentTime / totalDuration, 0, 1)
		local newCFrame = getInterpolatedCFrame(alpha)
		camera.CFrame = newCFrame
	end)
end

local function configureLighting(ambient, brightness, topColorShift, bottomColorShift, diffuseScale, specularScale, outdoorAmbient, clockTime, exposureCompensation)
	Lighting.Ambient = ambient
	Lighting.Brightness = brightness
	Lighting.ColorShift_Top = topColorShift
	Lighting.ColorShift_Bottom = bottomColorShift
	Lighting.EnvironmentDiffuseScale = diffuseScale
	Lighting.EnvironmentSpecularScale = specularScale
	Lighting.OutdoorAmbient = outdoorAmbient
	Lighting.ClockTime = clockTime
	Lighting.ExposureCompensation = exposureCompensation
end

-- Cutscene --
function CutsceneModule.introcutscene()

	Camera.CFrame = createCFrame(Vector3.new(31.987, 13.511, -1.683), Vector3.new(-24.155, 41.463, 0.29))

	configureLighting(
		Color3.new(0.196, 0.196, 0.196), 
		0,
		Color3.new(0, 0, 0),
		Color3.new(0, 0, 0),
		1,
		1,
		Color3.new(0.255, 0.255, 0.255),
		18,
		-0.5
	)

	local tweenData = {
		{
			duration = 10,
			startCFrame = createCFrame(Vector3.new(30.836, 12.545, 0.1), Vector3.new(-26.5, 36.9, 0))
		},
		Vector3.new(20.16, 10.127, -4.37),
		Vector3.new(20.61, 8.524, 0),
		createCFrame(Vector3.new(18.212, 6.139, -27.615), Vector3.new(0, 0, 0)),

		{
			duration = 3,
			startCFrame = createCFrame(Vector3.new(18.212, 6.139, -27.615), Vector3.new(0, 0, 0))
		},
		Vector3.new(15.885, 5.786, -37.673),
		Vector3.new(19.421, 6.312, -50.599),
		createCFrame(Vector3.new(20.773, 7.799, -57.712), Vector3.new(-0.167, -1.201, 0)),

		{
			duration = 4,
			startCFrame = createCFrame(Vector3.new(20.773, 7.799, -57.712), Vector3.new(-0.167, -1.201, 0))
		},
		Vector3.new(26.642, 9.576, -63.51),
		Vector3.new(33.708, 9.954, -70.248),
		createCFrame(Vector3.new(24.155, 10.804, -84.261), Vector3.new(-7.807, 34.146, 0))
	}

	playTweenSequence(Camera, tweenData)
end

-- End --

return CutsceneModule