Updating CFrame along Bezier curve via Renderstepped is causing odd behaviour

Hi, I’m trying to smoothly move a model to a series of nodes using positions generated from a Bezier curve to find positions to move to as the function continues to update via RenderStepped.

The issue is the behaviour is… very odd. I tried adapting my code to RenderStepped, but this has caused the model to move in unexpected ways, skipping many nodes and teleporting back and forth.

I’ve tried tweening, but each time a tween finishes there’s a brief pause before moving to the next node. I’ve also tried a for-loop, but the pause is even more dramatic.

Here’s my code:

local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local Workspace = game:GetService("Workspace")

repeat task.wait() until game.Players.LocalPlayer.Character

local train = Workspace.Train
local root = train.Root
local nodes = Workspace.Nodes

local currentIndex = 2
local countDirection = "up"
local progress = 0

local SPEED = 16

local function quadraticBezier(t: number, p0: Vector3, p1: Vector3, p2: Vector3): Vector3
	return (1 - t)^2 * p0 + 2 * (1 - t) * t * p1 + t^2 * p2
end

local function update(dt: number): ()
	local from = root.Position
	local to = nodes["Waypoint"..currentIndex].Position
	local distance = (to - from).magnitude

	local controlPoint
	if nodes:FindFirstChild("ControlPoint"..(currentIndex-1)..currentIndex) then
		controlPoint = nodes:FindFirstChild("ControlPoint"..(currentIndex-1)..currentIndex).Position
	else
		controlPoint = (nodes["Waypoint"..currentIndex].Position + nodes["Waypoint"..currentIndex-1].Position)/2
	end
	
	local alpha = progress/distance
	local nextPosition = quadraticBezier(alpha, from, controlPoint, to)
	local newCFrame = CFrame.new(nextPosition, nextPosition + CFrame.lookAt(root.Position, nextPosition).LookVector)
	root.CFrame = root.CFrame:Lerp(newCFrame, alpha)
	
	progress += 0.1
	if progress >= 1 then
		progress = 0
		currentIndex += 1
	end
end

RunService.RenderStepped:Connect(update)

I have a folder in workspace containing all my nodes and a model named “Train” for the model I’m moving.

Any help would be appreciated!

1 Like

I tried to recreate your setup in my own studio file and was able to find 2 issues that seem to fix the script.

  1. The line: local from = root.Position should be local from = nodes["Waypoint"..currentIndex-1].Position. This problem lies in the fact that the trains “from” position is constantly changing as the train moves, causing the bezier curve to not look as intended.
  2. The line: if progress >= 1 then should be if progress >= distance then. This problem lies in the fact that progress is counted from 0-1 meanwhile the alpha is counted from 0-distance, causing the train to move a much lesser distance than intended.

Another suggestion I’d also like to make is changing progress += 0.1 to progress += dt * speed (with speed being set somewhere else in the script) as this lets you control the speed of the train more precisely and lets the trains movement not get effected by framerates and/or lag spikes.

1 Like

This worked! Unfortunately, it brought back the “pausing” I described with tweening. However, updating the CFrame directly like so

root.CFrame = newCFrame

seems to have solved that issue as well!