Really rough flying train (snake-like) movement

I want to make a flying train system, and for that i’ve coded a movement system for the “train” that works but it really rough. This roughness grows as the train is scaled up.

Small size:

Larger size:

I havent tried any solutions from the devforum as I think this is a unique problem. I actually had a different method before this which was even more rough, so this is the best I think I can do on my own.

The actual model consists of the parts labelled ‘1’, ‘2’, ‘3’, and so on, representing the car number

This is the main LocalScript that controls the whole train (it doesnt matter if its client or server sided im just testing the system)

local RunService = game:GetService('RunService')

local Train = workspace:WaitForChild('Train') -- train model
Train:WaitForChild('1') -- wait for first car to load to avoid errors

local Cars = {} -- store the cars in a list



for i, v in pairs(Train:GetChildren()) do 
	if v:IsA('BasePart') and tonumber(v.Name) then
		Cars[tonumber(v.Name)] = v
	end
end

OldPosition = (Cars[1].CFrame - Cars[1].CFrame.LookVector * Cars[1].Size.Z/2).Position -- just to detect a change in position, probably a better way to do it but doesnt matter for now
CFrameHistory = {Cars[1].CFrame} -- table to keep track of the first car's path (as i want all the other cars to follow it)

local findBestCFrame = function(carNumber) -- Find best cframe for the specified car based on the first car path. This is done by comparing the back of the specified car to the 'fronts' of each of the cframes in the first car's history.
	local shortestDistance = 1000000
	local BestCFrame = nil

	local Pos1 = (Cars[carNumber].CFrame - Cars[carNumber].CFrame.LookVector * Cars[carNumber].Size.Z/2).Position


	for i, CF in pairs(CFrameHistory) do
		local Pos2 = (CF + CF.LookVector * Cars[1].Size.Z/2).Position
		if (Pos1 - Pos2).Magnitude < shortestDistance then
			shortestDistance = (Pos1 - Pos2).Magnitude
			BestCFrame = CF
		end
	end

	return BestCFrame
end

RunService.Stepped:Connect(function()
	if OldPosition ~= (Cars[1].CFrame - Cars[1].CFrame.LookVector * Cars[1].Size.Z/2).Position then
		table.insert(CFrameHistory, Cars[1].CFrame)

		for i = 1, #Cars - 1 do
			Cars[i+1].CFrame = findBestCFrame(i)
		end
	end

	OldPosition = (Cars[1].CFrame - Cars[1].CFrame.LookVector * Cars[1].Size.Z/2).Position
end)

Theres another script that purely controls the first car

local RunService = game:GetService('RunService')

local Player = game.Players.LocalPlayer
local Mouse = Player:GetMouse()

local trainSpeed = 15
local targetPosition = nil

wait(5) -- avoid starting too early for convenience

local Car1 = workspace:WaitForChild('Train'):WaitForChild('1')

Mouse.Move:Connect(function()
	if Mouse.Target then
		targetPosition = Mouse.Hit.Position
	end
end)

RunService.Heartbeat:Connect(function(dt)
	
	if targetPosition then
		Car1.CFrame = CFrame.new(Car1.Position, targetPosition)
		Car1.CFrame = Car1.CFrame + Car1.CFrame.LookVector * (dt/1) * trainSpeed
	end

end)

Any thoughts?

2 Likes

lol i fixed it myself ez

Here’s what I did if anyone needs to know.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.