Multiple moving models separate?

So I have these models that I got from the toolbox and i’m done with all the coding and stuff for this models. I’m trying to make a train that moves so I used TweenService to tween every model. So it tweened pretty well but they are somehow separating and sometimes even too close to each other.

  1. What is the issue? Include screenshots / videos if possible!
    The problem is, they are separating like they are leaving the others behind. But its unrealistic for trains in real life that their carriages separate. In real life while the train is moving, the train cars are not separating unless someone pulls the pin between the other cars, that will cause one car to be left behind. So its basically the same as in the problem as mine.

Here’s pictures:

Well I tried this topic:Limit CFrame to certain radius? to look for some solutions but any of them didn’t work. Because it’s an old topic, I tried asking for some things but I don’t get any answer.

THE TWEENING CODE:

	local TweenInfo = TweenInfo.new(
		Time,
		Enum.EasingStyle.Linear,
		Enum.EasingDirection.InOut,
		0,
		false,
		0
	)
	
	TweenService:Create(Carriage.PrimaryPart,TweenInfo,{CFrame = Node.CFrame + Vector3.new(0,Carriage.PrimaryPart.Size.Y/5,0)}):Play()

Plus I use nodes so the train will follow each node. The nodes contain position and rotation.

Here is the full code I know it’s very long:

This for the main script for the train to move

local TrainSuperClass = {}
TrainSuperClass.__index = TrainSuperClass

local Workspace = game:GetService("Workspace")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local ServerStorage = game:GetService("ServerStorage")
local Carriages = ServerStorage.Carriages
local RunService = game:GetService("RunService")

function TrainSuperClass.new(TrainModel,Nodes)
	local Train = {}
	
	setmetatable(Train,TrainSuperClass)
	
	Train.Nodes = Nodes
	Train.Dummy = TrainModel
	Train.Node = 0
	Train.LasCarriage = nil
	
	return Train	
end

function TrainSuperClass:Setup()
	print("Train has started!")
	self.Train = self.Dummy:Clone()
	self.Train.Parent = Workspace.Train
	self:Move()
end


function TrainSuperClass:GetTime(Distance)
	return Distance / self.Speed 
end


function TrainSuperClass:Carriages(Node)
	local Carriage = Carriages:GetChildren()[math.random(1,#Carriages:GetChildren())]:Clone()
	
	local All = Carriage:GetChildren()
	
	for A = 1,#All do
		if (All[A].Name~= self.Train.PrimaryPart and All[A]:IsA("BasePart")) then
			local NewWeld = Instance.new("Weld")
			NewWeld.Name = "Weld"
			NewWeld.Part0,NewWeld.Part1 = All[A],Carriage.PrimaryPart
			NewWeld.C0 = All[A].CFrame:inverse()
			NewWeld.C1 = Carriage.PrimaryPart.CFrame:inverse()
			NewWeld.Parent = Carriage.PrimaryPart
		end
	end
	
	if not (self.LastCarriage) then
		self.LastCarriage = self.Train
	end
	
	local Range = NumberRange.new(1,2)
	
	Carriage:SetPrimaryPartCFrame(self.LastCarriage:GetPrimaryPartCFrame() * CFrame.new(0,0,Carriage.PrimaryPart.Size.Z+2))
	
	self.LastCarriage = Carriage
	self.LastCarriage.Name = "LastCar"
	Carriage.Parent = Workspace.Train
    
    spawn(function()
	    wait()
	    self:MoveCarriage(Node,Carriage)
    end)	
	
end

function TrainSuperClass:MoveCarriage(NodeNum,Carriage)
	
	local Node = self.Nodes:FindFirstChild("Node"..NodeNum)
	
	if not (Node) then
		Carriage:Destroy()
		return
	end
	
	local Distance = (Carriage.PrimaryPart.CFrame.p - Node.CFrame.p).Magnitude
	local BetweenDistance = (Carriage.PrimaryPart.CFrame.p - self.LastCarriage.PrimaryPart.CFrame.p).Magnitude
		
	local Time = self:GetTime(Distance)
	
	local TweenInfo = TweenInfo.new(
		Time,
		Enum.EasingStyle.Linear,
		Enum.EasingDirection.InOut,
		0,
		false,
		0
	)
	
	TweenService:Create(Carriage.PrimaryPart,TweenInfo,{CFrame = Node.CFrame + Vector3.new(0,Carriage.PrimaryPart.Size.Y/5,0)}):Play()
	
	wait(Time)
	
	self:MoveCarriage(NodeNum + 1,Carriage)
	
end

function TrainSuperClass:Move()
	self.Node = self.Node + 1
	
	local Node = self.Nodes:FindFirstChild("Node"..self.Node)
	
	if not(Node) then
		self:Stop()
		return
	end	
	
	if self.Node == 1 then
		local All = self.Train:GetChildren()
		
		for A = 1,#All do
			if (All[A].Name~= self.Train.PrimaryPart and All[A]:IsA("BasePart")) then
				local NewWeld = Instance.new("Weld")
				NewWeld.Name = "Weld"
				NewWeld.Part0,NewWeld.Part1 = All[A],self.Train.PrimaryPart
				NewWeld.C0 = All[A].CFrame:inverse()
				NewWeld.C1 = self.Train.PrimaryPart.CFrame:inverse()
				NewWeld.Parent = self.Train.PrimaryPart
			end
		end
		wait()
		self.Train:SetPrimaryPartCFrame(Node.CFrame + Vector3.new(0,self.Train.PrimaryPart.Size.Y/2,0))
		
		for i = self.CarriagesCount,1,-1 do
			self:Carriages(self.Node)
		end
		
		self:Move()	
		
		return
	end
	
	local Distance = (self.Train.PrimaryPart.CFrame.p - Node.CFrame.p).Magnitude
	
	local Time = self:GetTime(Distance)
	
	local TweenInfo = TweenInfo.new(
		Time,
		Enum.EasingStyle.Linear,
		Enum.EasingDirection.InOut,
		0,
		false,
		0
	)
	
	local Range = NumberRange.new(1.5,2)
	
	TweenService:Create(self.Train.PrimaryPart,TweenInfo,{CFrame = Node.CFrame + Vector3.new(0,self.Train.PrimaryPart.Size.Y/2,0)}):Play()
	
	wait(Time)
	self:Move()
	
	if (self.Node == 10) then
		print("Node is ten yen....")
	end
end


function TrainSuperClass:Stop()
	self.LastCarriage = nil
	self.Train:Destroy()
	self.Train.Parent = nil
	self.Node = 0
	print("Train has finished! Starting setup...")
	wait(14)
	self:Setup()
end

return TrainSuperClass

The script inside the model of the train (the main train car, the very front)

local ServerScriptService = game:GetService("ServerScriptService")
local SuperClasses = ServerScriptService.SuperClasses

local TrainSuperClass = require(SuperClasses.TrainSuperClass)

local TrainClass = {}
TrainClass.__index = TrainClass

setmetatable(TrainClass,TrainSuperClass)

function TrainClass.new(...)
	local self = TrainSuperClass.new(...)
	
	setmetatable(self,TrainClass)
	
	self.Speed = 60
	self.CarriagesCount = math.random() + 5 * 2 - 3 -- Random amount of train cars
	
	return self
end

return TrainClass

Sorry for that long line, its very a long code and its kinda comples if you asked me.
So if you have any suggestions reply below. Any of you know any ideas? Let me know in the replies. Do you know any ways of calculating the distance between each train car? Reply below :slightly_smiling_face:

1 Like

Instead of using PrimaryPartCFrame, try welding everything to the primary part and just tweening the primary part’s cframe
Also try tweening on all the clients instead of the server with remoteEvents.

It seems like its just lag lol.

Have a nice day, -HiddenKaiser

Also just finding from the middle of each car you would just get a part of where you want to measure distance from. This could be the connectors on each train, make a front and back measurePart, and measure distance accordingly.

1 Like

Well I use PrimaryPartCFrame to move each train car. Without PrimaryPartCFrame, I ain’t able to move them.

Edit: Also what do you mean tweening on all the clients? FireAllClients() in an remoteEvent?
Edit2: Or maybe using lerps combined with magnitude like not too close and not too far. How would I do that?

1 Like

Alright so let me demonstrate a bit more with a turret I made.
–To answer your first question, I answered this already;–

I just was looking through your script and noticed you are welding everything to the primary part, ignore the part about the welding.

“try welding everything to the primary part and just tweening the primary part’s CFrame.”

Basically you would weld every part to the primary part and unanchor everything but the primary part. Here, I made script for you to run in command bar:

Weld Code
local model = workspace:WaitForChild("TrainCar1") -- Specificy the traincar you want to weld together
 if model == nil then warn("Please specifiy a valid model") return end

 local primary = model.PrimaryPart

 if primary then
primary.Anchored = true
for i,v in pairs(model:GetDescendants()) do
	if v ~= primary and v:IsA("BasePart") then
		local oldWeld = v:FindFirstChildWhichIsA("Weld")
		if oldWeld then oldWeld:Destroy() end
		local w = Instance.new("WeldConstraint", v)
		if v.Anchored == true then v.Anchored = false end
		w.Part0 = v
		w.Part1 = primary
	end
end
 else
warn("Please set a primary part for " ..model.Name .." (" ..model:GetFullName() ..")")
end

the reason you want to do this is because setting the primary part cframe that much is horribly inefficient and will cause lag, especially since its being done on the server.
Also, for client tweening, yes you would fire all clients and on a client script you would just tween the primary part like so:

REMOTEVENTHERE.OnClientEvent:Connect(function(ENDCFRAME, PRIMARY)
      local tweenInfo1 = TweenInfo.new(5, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut)

      tweenObject1 = tweenS:Create(PRIMARY, tweenInfo1, {CFrame = ENDCRAME})

      tweenObject1:Play()
 end)
1 Like

I weld each train car with a primarypart.
Edit: So I put the onclientevent function in an localscript but where do I parent it? I don’t know how to use :FireAllClients() and what parameters i should put in it? Where should I put the :FireAllClients() thing?

actually the tweeninfo should be this:

	local TweenInfo = TweenInfo.new(
		Time,
		Enum.EasingStyle.Linear,
		Enum.EasingDirection.InOut,
		0,
		false,
		0
	)
	
	TweenService:Create(Carriage.PrimaryPart,TweenInfo,{CFrame = Node.CFrame + Vector3.new(0,Carriage.PrimaryPart.Size.Y/5,0)}):Play()

Did you try what was suggested and if so what happens now?

1 Like

I am trying his suggestion but i Don’t know much of remoteEvents and RemoteFunctions.

Have you checked out these references

Tutorials

1 Like

I never thought of that. But I am tweening into client already.

You would parent in starterplayerscripts, i was sleeping, i apologize.

1 Like

So the onclientevent function in the localscript should be parented in starterplayerscripts? But what line of code i should type the FireAllClients() thing? I have showed you the full code in the question already.