Refining train physics

We’re looking to refine how trains move in the game Snow Hill Lines, as currently they shudder like crazy at high speeds, sometimes get stuck at corners, etc. Throughout this we’ve been using BodyVelocity to tell the train how to move, and now we can clearly see this isn’t working very well.

As per this post asking relitavely the same thing regarding train physics, it’s agreed that CFraming is the way to go. However, it’s an extremely foreign topic to us and we have no idea how the CFrame implementation would actually work.

So i’m asking, how does CFrame get the trains moving? Would it require any additional parts, or can we use the existing track?

7 Likes

@DevOfLua has a good couple of videos on this:

6 Likes

I cannot call these videos good because I don’t explain what I do I just give out the code without them understanding what does etch line do.

I am not going to go into details but I will explain the main parts of the code welding and moving.

First of all, I create a part called PrimaryPart which will be the same size as the train then I loop through the whole train check if the object is a basepart or not if it is I weld it to a part called PrimaryPart after that is done I get a distance between 2 nodes (Vector1 - Vector2).Magnitude
after that, I divide distance with the speed to get the time after that you have to tween the PrimaryPart to the next node.

Weld

       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

Time

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

Move

	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.In,
		0,
		false,
		0
	)
	
	TweenService:Create(self.Train.PrimaryPart,TweenInfo,{CFrame = Node.CFrame + Vector3.new(0,self.Train.PrimaryPart.Size.Y/2,0)}):Play()
	
	wait(Time)
3 Likes

Did you read @EchoReaper’s solution to the problem? He was even kind enough to supply a repro: How do I make my minecart stay on the rails? - #17 by EchoReaper

Basically you have one part (Part A) that follows a bunch of “Nodes” on your train tracks. Then you use a PhysicsConstraint to align the rest of the train with Part A.

You could do something similar with SetPrimaryPartCFrame with nodes, but keep in mind that won’t necessarily stop the stuttering. That is caused by replication latency which would still exist with CFrame.

In fact, if you use PhysicsConstraints you can just set your train’s NetworkOwner to the driver and achieve smoother-looking movement.

4 Likes