I’m here to see if anyone has a better way to make a train car follow another train car that is being tweened.
Right now, I am making each car tween individually, but I think there is a better way than to do this
However, I don’t mean that they are both parts in the same position, I mean one has to be following the other from behind, for example, 2 cars on a train. The 1st car is moving, and the 2nd car is moving along with it (following it) from behind.
You could tween the main part and change the other part’s CFrame inside of a RunService loop in-order to make them follow the main part at a fixed offset, using CFrame’s ToWorldSpace method:
-- Server Script in ServerScriptService
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local partA = Instance.new("Part")
partA.Position = Vector3.new(0, 4, 0)
partA.Anchored = true
partA.Parent = Workspace
local partB = Instance.new("Part")
partB.Anchored = true
partB.Parent = Workspace
local tweenInfo = TweenInfo.new(4, Enum.EasingStyle.Linear, Enum.EasingDirection.In)
local tween = TweenService:Create(partA, tweenInfo, {Position = partA.Position + Vector3.new(8, 0, -16)})
local partLength = partA.Size.Z
local offsetCFrame = CFrame.new(0, 0, partLength)
print(offsetCFrame)
local connection = RunService.Heartbeat:Connect(function()
partB.CFrame = partA.CFrame:ToWorldSpace(offsetCFrame)
end)
tween:Play()
tween.Completed:Wait()
connection:Disconnect()
Yes, the partLength variable determines the offset between each part
If each train car has the same length, then it should be set to the train car part’s Z size (this is assuming that -Z axis is the forward axis of your train car) + (the desired air gap you want between each part (in studs) * 2)
Oh ya, I noticed something, for your code right, when the 2 train cars go onto a turn, I assume they turn as one whole block, However I need the train cars to turn along with the track.
Do you have an idea on how to go about doing this. I was thinking to list down every single CFrame that A was in, and then B would follow the CFrame listed, but with a delay so it would go something like that
RunService’s Heartbeat loop is a loop that runs once a frame, although after physics is simulated. It’s essentially very similar to a regular while true do loop, but it runs the function connected to it in a separate thread, so it doesn’t block the code written below it from running until the loop is broken
CFrame:ToWorldSpace is a method that takes a CFrame value and converts it from object space into world space. Let’s say a chair is placed 10 studs to the right of a table, and the table’s position is 4, 2, 5. This means that the chair’s world position is at 14, 2, 5, and the chair’s position relative to the table’s position is 10, 0, 0. This essentially means that object space is the position or CFrame of an object relative to the position or CFrame of another object, which is ideal to use for a situation like yours
That’s due to the fact that, by default, the origin of a part’s CFrame and position is its center. This means that if you don’t multiply the value by 2 and you’d like the air gap to be 1 stud, the visible gap between each part would actually be 0.5 studs instead
That would be quite hard to do using CFrame alone, I’d recommend using physics constraints instead. You could also use Vector3Curves, but this would require a lot of tinkering in-order to match the curve to the tracks as closely as possible
What does the function Lerp return, and what do the arguements stand for (Sorry for asking, but I could not understand the definition when i search on google)
lerp() stands for “Linear Interpolation”. On Roblox, is used to gradually move one position, rotation, or other property toward a target value by a specific percentage each frame.
CFrame:lerp(target, percentage) works by creating a CFrame that is some percentage of the way between the current CFrame and the target.
percentage = 0 gives the starting CFrame.
percentage = 1 gives the target CFrame.
Values between 0 and 1 blend the two CFrames proportionally, so percentage = 0.5 moves halfway between them.
In the example I provided to you, 0.1 represents 10%, so the Train2 will move 10% closer to Train1.
One correction though: Since in the code provided the percentage’s value is constant, the reason why Train1 is moving closer to Train2’s position is due to the fact that you’re changing the target position
The intended way to use lerp functions is to vary the percentage rather than the starting value and the target value
Plus when you multiply a CFrame by another CFrame, it’s the same as using CFrame1:ToWorldSpace(CFrame2), so the only difference between your code and mine is how we’re handling the air gap between each part
Yep, you’re right! I made some changes, you’re free to suggest extra modifications.
--// Services
local RunService = game:GetService("RunService")
local tweenService = game:GetService("TweenService")
--// Variables
local trainCar1 = game.Workspace.Train1
local trainCar2 = game.Workspace.Train2
local offset = CFrame.new(20, 0, 0)
local connection
local myTween
local function updateFollower()
connection = RunService.Heartbeat:Connect(function()
local targetPosition = trainCar1.CFrame:ToWorldSpace(offset)
trainCar2.CFrame = trainCar2.CFrame:lerp(targetPosition, 0.9)
end)
end
local function onTweenCompleted()
connection:Disconnect()
end
delay(5, function()
myTween = tweenService:Create(trainCar1, TweenInfo.new(20), {Position = trainCar1.Position + Vector3.new(200, 0, 0)})
coroutine.wrap(updateFollower)()
myTween:Play()
myTween.Completed:Connect(onTweenCompleted)
end)
You’re free to remove delay() obviously, I simply added it to see the animation with a delay upon client added.
Does anyone have a solution to this, Or how i would be able to code this?
Right now I am thinking updating B position to A but with a delay to make it seem like its following it but it does not work due to some flaws
Does anyone know of a way to code this? I just need ideas
I guess you’re using models for Trains. You’d have to work with PrimaryParts. And be sure to modify the Offset vector (x, y, z) depending on your model.
You could also try animating the train if you’d prefer not to use physics constrains. You can make the train stop by setting the AnimationTrack’s speed to 0, and you can use animation events to make the train stop when it reaches a station
Here’s an example of how the code will need to be if you choose to use animations:
-- Server Script inside of the train's model
local ANIMATION_SPEED = 1 -- This affects the speed of your train
local STATION_WAIT_DURATION = 4 -- How long the train will wait at a station
local train = script.Parent
local animator = train:FindFirstDescendant("Animator")
if animator and animator:IsA("Animator") then
local animation = Instance.new("Animation")
animation.AnimationId = "rbxassetid://" -- Remember to set this to the animation ID
local animationTrack = animator:LoadAnimation(animation)
-- For this to work, the animation events will need to be named StationReached
animationTrack:GetMarkerReachedSignal("StationReached"):Connect(function()
animationTrack:AdjustSpeed(0)
task.wait(STATION_WAIT_DURATION)
animationTrack:AdjustSpeed(ANIMATION_SPEED)
end)
animationTrack:Play(nil, nil, ANIMATION_SPEED)
else
warn("Animator not found as a descendant of the train model")
end