How to curve trains?

Hello :wave: me and @sasial are working on trains, and we have been making a node system ( I recommend to check this link How to rotate rays? - #38 by sasial )
Now we’re currently struggling with rotating every single car separately in curves. If you have any ideas to solve this problem, then it would be much appreciated. We are currently having two main problems.

  1. Train doesn’t want to align when spawning
  2. The train is curving in really weird way, I’ll send you picture.

and after the curve, train is looking normally.
image

Here is also the train script.

local trainoff = Vector3.new(1,9,0)
local offpos = trainoff + Vector3.new(0,0,script.OffZ.Value)
for i,v in pairs(script.Parent.Cars:GetChildren()) do
    local PrimaryPart = script.Parent.Cars["Car"..i]
    PrimaryPart:SetPrimaryPartCFrame(CFrame.new(workspace.Tracks.Start.PrimaryPart.Position + offpos))
    offpos = offpos + Vector3.new(0,0,script.Parent.Cars["Car"..i].Box.Size.Z)
end
--- Train Script Below
local RunService = game:GetService("RunService")

local Grinding = true

local function GetDist(p)
	return (p.Node1.WorldPosition - p.Node0.WorldPosition).Magnitude
end

local function GetNewPosition(p, a)
	return (p.Node1.WorldPosition - p.Node0.WorldPosition) * a + p.Node0.WorldPosition
end

local function GetNewNode(currentNode, dir)
   return currentNode.Part.NextNode.Value
end

local function GrindRail(Train, RailParts, CurrentPart)

	--AlignPosition.Attachment1.Position = AlignPosition.Attachment0.WorldPosition
	
	local CurrentNode = RailParts[CurrentPart]
	
	--alpha between Node0 and Node1
	local a = 0--dist2.Unit:Dot(dist1)/dist2.Magnitude
	--All variables should be found by now!

	
	
	--AlignPosition.Enabled = true
	
	local connection
	local CurrentRatio = GetDist(CurrentNode.Part)
	
	connection = RunService.Heartbeat:Connect(function(dt)
		a = a + dt*script.Speed.Value/CurrentRatio
		print(a)
		if a > 1 then
			repeat
				local newPart = GetNewNode(CurrentNode, 1)
				if newPart then
					CurrentNode = RailParts[newPart]
					local NewRatio = GetDist(newPart)
					a = a - 1
					a = a * NewRatio/CurrentRatio
					CurrentRatio = NewRatio
				else
					connection:Disconnect()
					break
				end
			until a <= 1
		elseif a < 0 then
			repeat
				local newPart = GetNewNode(CurrentNode, -1)
				if newPart then
					CurrentNode = RailParts[newPart]
					local NewRatio = GetDist(newPart)
					a = a + 1
					a = a * NewRatio/CurrentRatio
					CurrentRatio = NewRatio
				else
					connection:Disconnect()
					break
				end
			until a >= 0
		end
		
		if CurrentNode and Grinding then
			local dir = (CurrentNode.Part.Node1.WorldPosition - CurrentNode.Part.Node0.WorldPosition).Unit
			--[[Train.PrimaryPart.CFrame = Train.PrimaryPart.CFrame:Lerp(CFrame.new(
				GetNewPosition(CurrentNode.Part,a) + Vector3.new(0,10,0),
				GetNewPosition(CurrentNode.Part,a) + Vector3.new(0,10,0) + dir
			), 0.1)--]]
			for i,v in pairs(script.Parent.Cars:GetChildren()) do
		        local car = script.Parent.Cars["Car"..i]
		--        print(getdistancetonode(currentpart,dir,i))
		    --    print(i)
	            --print(ii.." "..i)
				local offpos = trainoff + Vector3.new(0,0,script.OffZ.Value)
	            car:SetPrimaryPartCFrame(CFrame.new(
	                car.PrimaryPart.Position
	            ):Lerp(CFrame.new(
                GetNewPosition(CurrentNode.Part,a) + offpos + Vector3.new(-1,0,73.41*i),
                GetNewPosition(CurrentNode.Part,a) + offpos + dir + Vector3.new(-1,0,73.41*i)
	            ), 0.1))
				offpos = offpos + Vector3.new(0,0,73.41*i)
			wait(0.001) 
			end
			wait(1/script.Speed.Value)
			--AlignPosition.Attachment1.Position = GetNewPosition(CurrentNode.Part,a) + Vector3.new(0,3,0)
		else
			--AlignPosition.Enabled = false
			wait(2)
			Grinding = false
		end
		
	end)
end

wait(1)
print("waiting for setup...")
wait(2.3)
print("ready!")

local train = script.Parent

local RailParts = {}
local RailModel = workspace.Tracks

for _, part in ipairs(RailModel:GetChildren()) do
	
	RailParts[part.PrimaryPart] = {
		Part = part.PrimaryPart
	}
end

GrindRail(train, RailParts, workspace.Tracks.Start.PrimaryPart)

Thank you for your help, it’s much appreciated.

EDIT: Grammar

2 Likes

You could make the train face the right way with CFrame.
The part where you set the CFrame of the train could be rewritten into the Nodes CFrame * CFrame.new(offpos).
Something like that.

3 Likes

Thank you for your help, do you have any idea that will help us fix this problem?

1 Like