Moving a train cart via lerp using acceleration

So my problem is, I have a train which moves from one “node” to the next, these nodes contain among other things a CFrame and a Speed which the train needs to reach when it has reached the node.
The train already works fine, I use lerp, the delta being the time from one node to the other, however that way the speed changes instantaneously, I want smooth acceleration, deceleration without adding 1000 nodes.

I have no idea how to re-script the new lerping function using acceleration because the time it takes to reach the next node changes because the speed does too.

Start = function(cart, journey, pref)
	local speed = 0
	cart:SetPrimaryPartCFrame(journey[1])
	
	for i = 2, #journey do
		--speed starts at 0
		--journey[i] contains .Node as CFrame and .Speed as number
		--journey[i] is the next node, journey[i-1] the current one
		local distance, duration = module.GetDistanceDelta(
			journey[i-1],journey[i]
		)
				
		if journey[i].Speed > journey[i-1].Speed then
			local acceleration = (math.pow(journey[i-1].Speed, 2) - math.pow(journey[i].Speed, 2)) / (2 * distance)
			speed = speed + acceleration
			print("Acceleration of ", pref.LineName," ", acceleration)
		else
			local deceleration = math.pow(journey[i].Speed, 2) - math.pow(journey[i-1].Speed, 2) / (2 * distance)
			speed = speed - deceleration
			print("Deceleration ", pref.LineName, " ", deceleration)
		end

		--lerping happens below	
		local start = journey[i-1].Node
		local spent = 0
		while spent < 1 do
			spent = spent + runService.RenderStepped:wait() / duration
			cart:SetPrimaryPartCFrame(start:lerp(journey[i].Node, spent))
		end
		cart:SetPrimaryPartCFrame(journey[i].Node)
	end
end
3 Likes

Just for reference, this would replace the code below your comment, lerping happens below. All you have to do is define distance and duration correctly.

	-- new variable!
	local speed = distance / duration
	
	
	local start = journey[i-1].Node
	local spent = 0
	while spent < 1 or spent > 0 do -- just in case you want the train to go backwards
		local dt = runService.RenderStepped:Wait()

		-- using a clamp so that you don't need to SetPrimaryPartCFrame at the end
		spent = math.clamp(spent + speed * dt, 0, 1)
		
		-- use this space to calculate a new speed, 
		-- especially if you want it to decelerate or accelerate mid-loop

		cart:SetPrimaryPartCFrame(start:Lerp(journey[i].Node, spent))
	end
	-- no need for SetPrimaryPartCFrame here, the while loop covers it!
	-- continue on with the rest of your code...

I also made a cool node pather here, if that helps. Pay attention to the GrindRail function, especially the RunService.RenderStepped connection I make in it. This was a solution for rail grinding, but trains also ride on rails, so you can use the same concept:

1 Like

Another question is…
How would I calculate the total time it takes between two nodes?

Let t be time while x is final position and y is start position. Then divide by velocity or speed.

t = ((x-y).magnitude)/speed

Wouldn’t that only work if the speed was constant?