Hello. Quick question, So I have a train here, I just got it from toolbox. It works fine and everything. I have a train system to make it move through the map.
So today It’s been working fine, its all smooth cuz’ I made the train system client sided. But it is almost smooth until I noticed that there are stuttering movement between the train cars. When they are turning through the curve rails. I do not understand why it does this, it’s supposed to only stutter when its moving on the server but no, I already made my train client sided. I don’t know what’s causing the stuttering on the train car movement.
This is what happens when the train cars are turning on the curve:
https://streamable.com/9rxupr << Click that link to watch the vid
As you can see, when the train cars are turning through the curves, you can notice a stuttering movement in each of the train cars, including the front train cars. I don’t know what to do to smooth it out. You got any ideas to smooth out this entire train movement? I will be showing the code for the train system:
This code is a module. It’s in a module script required in ReplicatedStorage by a client script:
local trainModule = {}
math.randomseed(tick())
-- \\ Settings // --
local waitBeforeSpawn = 2.5 + math.random()
local trainSpawnTime = 30 -- Cooldown of train spawning
local rearFacingTrain = true
local minCarriages = 5
local maxCarriages = 8
local loop = true
local stop = false
local speed = script.Parent.Speed
--------------------
local serverStorage = game:GetService("ReplicatedStorage")
local trains = serverStorage.TrainObjects.Trains
local carriages = serverStorage.TrainObjects.Carriages
local train = trains.Train
local workspaceTrains = workspace.Trains
local connections = {}
local runService = game:GetService("RunService")
function trainModule:Sounds(Train)
print("SOUNDS")
Train.PrimaryPart.Engine:Play()
Train.PrimaryPart.Horn:Play()
Train.PrimaryPart.Bell:Play()
end
function trainModule:GetTime(distance, speed)
return distance / speed
end
function trainModule:MoveModel(model,start,End,AddBy)
for i = 0, 1, AddBy do
if not model.PrimaryPart then break end
model:SetPrimaryPartCFrame(start:Lerp(End,i))
runService.Heartbeat:Wait()
end
end
function trainModule:MoveCarriages()
for i, v in pairs(self.Carriages) do
coroutine.wrap(function()
self:MoveCarriage(v, self.Node)
end)()
end
end
function trainModule:MoveCarriage(carriage, nodeNum)
local node = self.Nodes:FindFirstChild("Node"..nodeNum)
if not node then carriage:Destroy() return end
local cf1 = node.CFrame * CFrame.new(0, carriage.PrimaryPart.Size.Y / 2, 0)
local cf2 = carriage.PrimaryPart.CFrame
local distance = (cf1.p - cf2.p).Magnitude
local Time = self:GetTime(distance, speed.Value)
local addBy = 1 / Time
self:MoveModel(carriage, carriage.PrimaryPart.CFrame, cf1, addBy)
self:MoveCarriage(carriage, nodeNum + 1)
end
function trainModule:Move(nodeNum)
local node = self.Nodes:FindFirstChild("Node"..nodeNum)
if not node then self.Train:Destroy() return end
if nodeNum == 1 then
self:MoveCarriages()
end
local cf1 = node.CFrame * CFrame.new(0, self.Train.PrimaryPart.Size.Y / 2, 0)
local cf2 = self.Train.PrimaryPart.CFrame
local distance = (cf1.p - cf2.p).Magnitude
local Time = self:GetTime(distance, speed.Value)
local addBy = 1 / Time
--if nodeNum == 4 then stop = true if stop == true then return end wait(5) print("SHYDytasfd") end
self:MoveModel(self.Train, self.Train.PrimaryPart.CFrame, cf1, addBy)
self:Move(nodeNum + 1)
end
function trainModule:Spawn()
self.Train = train:Clone()
self.Train.Parent = workspaceTrains
self.Nodes = workspace.Nodes
self.Node = 1
self.Carriages = {}
for i, v in pairs(connections) do
v:Disconnect()
end
local node = self.Nodes:FindFirstChild("Node"..self.Node)
if not node then error("Could not find Node"..self.Node) return end
for i, v in pairs(self.Train:GetDescendants()) do
if v:IsA("BasePart") then
if v ~= self.Train.PrimaryPart then
local Weld = Instance.new("Weld")
Weld.Part0 = v
Weld.Part1 = self.Train.PrimaryPart
Weld.C0 = v.CFrame:inverse()
Weld.C1 = self.Train.PrimaryPart.CFrame:inverse()
Weld.Parent = self.Train.PrimaryPart
end
end
end
self.Train:SetPrimaryPartCFrame(node.CFrame * CFrame.new(0, self.Train.PrimaryPart.Size.Y / 2, 0))
self.LastCarriage = self.Train
self:Sounds(self.Train)
if rearFacingTrain then
self.BackWardsTrain = train.RearTrain.Value:Clone()
self.BackWardsTrain.Parent = workspaceTrains
for i, v in pairs(self.BackWardsTrain:GetDescendants()) do
if v:IsA("BasePart") then
if v ~= self.BackWardsTrain.PrimaryPart then
local Weld = Instance.new("Weld")
Weld.Part0 = v
Weld.Part1 = self.BackWardsTrain.PrimaryPart
Weld.C0 = v.CFrame:inverse()
Weld.C1 = self.BackWardsTrain.PrimaryPart.CFrame:inverse()
Weld.Parent = self.BackWardsTrain.PrimaryPart
end
end
end
self.BackWardsTrain:SetPrimaryPartCFrame(self.Train.PrimaryPart.CFrame * CFrame.new(0, -(self.Train.PrimaryPart.Size.Y - self.BackWardsTrain.PrimaryPart.Size.Y) / 2, (self.Train.PrimaryPart.Size.Z / 1.90) + (self.BackWardsTrain.PrimaryPart.Size.Z / 1.90)))
self.LastCarriage = self.BackWardsTrain
table.insert(self.Carriages, self.BackWardsTrain)
end
local availibleCarriages = {}
for i, v in pairs(carriages:GetChildren()) do
if v.Train.Value == train then
table.insert(availibleCarriages, v)
end
end
local carriagesToMake = math.random(minCarriages, maxCarriages)
for i = 1, carriagesToMake do
self.CurrentCarriage = availibleCarriages[math.random(1, #availibleCarriages)]:Clone()
self.CurrentCarriage.Parent = workspaceTrains
for i, v in pairs(self.CurrentCarriage:GetDescendants()) do
if v:IsA("BasePart") then
if v ~= self.CurrentCarriage.PrimaryPart then
local Weld = Instance.new("Weld")
Weld.Part0 = v
Weld.Part1 = self.CurrentCarriage.PrimaryPart
Weld.C0 = v.CFrame:inverse()
Weld.C1 = self.CurrentCarriage.PrimaryPart.CFrame:inverse()
Weld.Parent = self.CurrentCarriage.PrimaryPart
end
end
end
self.CurrentCarriage:SetPrimaryPartCFrame(self.LastCarriage.PrimaryPart.CFrame * CFrame.new(0, -(self.LastCarriage.PrimaryPart.Size.Y - self.CurrentCarriage.PrimaryPart.Size.Y) / 2, (self.LastCarriage.PrimaryPart.Size.Z / 1.92) + (self.CurrentCarriage.PrimaryPart.Size.Z / 1.92)))
self.LastCarriage = self.CurrentCarriage
table.insert(self.Carriages, self.CurrentCarriage)
end
if self.Train:FindFirstChild("KillerBrick") then
local killConnection = self.Train:FindFirstChild("KillerBrick").Touched:Connect(function(hit)
if hit.Parent:FindFirstChild("Humanoid") then
hit.Parent:FindFirstChild("Humanoid").Health = 0
elseif hit.Parent.Parent:FindFirstChild("Humanoid") then
hit.Parent.Parent:FindFirstChild("Humanoid").Health = 0
end
end)
end
self:Move(self.Node)
wait(trainSpawnTime)
if loop then
self:Spawn()
else
return
end
end
function trainModule:Start()
wait(waitBeforeSpawn)
print("Train Packet Creation")
for i, v in pairs(workspace.Nodes:GetDescendants()) do
if v:IsA("BasePart") then
v.Transparency = 1
end
end
self:Spawn()
end
return trainModule
This script will use a remoteEvent in order to replicate for everyone. It will use a remoteEvent and fire it to all clients with a string value acting as a “Request Type” for security:
wait(5)
print("Remote event fired!")
game.ReplicatedStorage.CreateTrain:FireAllClients({reason = "SpawnTrain"}) -- this table contains a string value. The string value acts as a "Request Type" so when a script picks it up, it will check if the request is valid, otherwise it will not pickup.
The client script will pickup the event:
local ReplicatedStorage = game.ReplicatedStorage
ReplicatedStorage.CreateTrain.OnClientEvent:Connect(function(RequestType)
if RequestType.reason == "SpawnTrain" then
for i, v in next, game.ReplicatedStorage.TrainSystem.Modules:GetChildren() do
if v:IsA("ModuleScript") then
local thread = coroutine.create(function()
local module = require(v)
module:Start()
end)
coroutine.resume(thread)
end
end
else
print("Invalid request")
end
end)
—EXTRA DETAILS—
I use strings on remote events just for organized stuff and security as well. So the scripts that will be picking up events will not just accept it unless the reason for firing an event is valid.
So that’s all about it. If you got any more questions, ask me below. Got any ideas, suggestions, examples, anything to share to me? tell me in the replies below
All help is appreciated!
Thanks in advance