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?