Hello! First of all, I want to point out that I have a working train movement system that works with TweenService.
A problem I encountered with longer coaches is that they don’t turn realistically. And if you think the curve is too sharp - it’s not, it works fine with physics based trains. (with BodyVelocity) GIF
Is there any way I can make the coaches turn realistically? (Like Stepford County Railway and other games) I tried using 2 parts in each coach that I would tween but unfortunately that didn’t work.
It looks like you’re determining the angle of the coach based on the track at its center. You should instead be setting an angle that keeps the front and back wheels on the track.
So, I have a RootPart that is the length of a coach, and its CFrame is tweened to the next node.
function TrainSuperClass:CreateTween(carriage)
local node = self[carriage.Name].Node
if not node then
warn(carriage.Name.." does not have next node set")
return
end
local distance = (carriage.PrimaryPart.CFrame.p - node.CFrame.p).Magnitude
local Time = self:GetTime(distance)
local tweenInfo = TweenInfo.new(
Time,
Enum.EasingStyle.Linear,
Enum.EasingDirection.Out,
0,
false,
0
)
if type(self[carriage.Name].Tween) == "userdata" then
self[carriage.Name].Tween:Cancel()
end
self[carriage.Name].Tween = TS:Create(carriage.PrimaryPart, tweenInfo, {CFrame = node.CFrame})
self[carriage.Name].Tween:Play()
end
Have a RootPart at the front and rear wheels. Space the nodes correctly so that they will line up with the RootParts. Then as you tween the rear wheels to a new node, the front wheels should be at the next node or maybe the one after that, depending on how you space it.
The CFrame’s position is determined by the rear node and the look vector is the unit vector of the difference between the rear and front node’s position.
You don’t need the front RootPart. You just need the rear one, but define the CFrame as CFrame.new(node1, node2)
where node1 is where the rear wheels are and node2 is where the front wheels are (a few nodes ahead of node1 depending on the coach length)
The CFrame’s position is determined by the rear node and the look vector is the unit vector of the difference between the rear and front node’s position.
Can you explain this part a bit more, please? Sorry if I am being annoying but I haven’t worked with more complex CFrame stuff before.
When you pass two positions into CFrame.new it creates a CFrame at the first position pointing towards the second position. (By node1 I mean node1.Position and same for node2)
Alternatively you can use CFrame.fromMatrix as described in Developer CFrame tutorial. It’s more powerful but you probably don’t need it here.
So, which part am I gonna be actually tweening? And if it’s the rear RootPart of the coach, then would I do something like this?
--RootPart2 is the rear RootPart, RootPart1 is the front RootPart; PrimaryPart of the coach is the rear RootPart
local cframe = CFrame.new(carriage.RootPart2.Position, carriage.RootPart1.Position)
local distance = (cframe.p - node.CFrame.p).Magnitude
local Time = self:GetTime(distance)
local tweenInfo = TweenInfo.new(
Time,
Enum.EasingStyle.Linear,
Enum.EasingDirection.Out,
0,
false,
0
)
self[carriage.Name].Tween = TS:Create(carriage.PrimaryPart, tweenInfo, {CFrame = node.CFrame})
self[carriage.Name].Tween:Play()