Optimize train script

  1. What do you want to achieve? Hello, i got a train system that i found online and i modified a bit so it works with my game.

  2. What is the issue? The issue is that the script use RunService.Heartbeat and it generate a enormous amount of lag.

This is the script

local ServerScriptService = game:GetService("ServerScriptService")
local TweenService = game:GetService("TweenService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local rs = game:GetService("RunService")

local ClientTween = require(ServerScriptService.Core.Modules.ClientTween)

local nodes = script.Parent.Nodes
local nodeReady = script.Parent.NodeReady

repeat wait() until nodeReady.Value 

function InitializeTram(Tram)
	local train = Tram
	
	local curSpeed = train.CurrentSpeed
	
	local startingNode = train.StartingNode
	
	local loco = train.Locomotive
	local front = loco.Front
	local back = loco.Back
	local base = loco.Base
	local gap = (front.Position - back.Position).Magnitude
	local loco_model = loco.Model
	
	local CurrentStation = nil
	
	front.Current.Value = nodes:FindFirstChild("Node_" .. startingNode.Value)
	back.Current.Value = nodes:FindFirstChild("Node_" .. startingNode.Value)
	
	local function NextNode(target)
		local curNode = target.Current
		local nextNode = curNode.Value.NextNode.Value

		curNode.Value = nextNode
	end
	
	local function Move(target, deltaTime)
		local curNode = target.Current.Value
		local nextNode = target.Current.Value.NextNode.Value

		if not nextNode then
			curSpeed.Value = 0
			warn("UNABLE TO FIND NEXT NODE!")
			return
		end

		local distanceTraveledOnSection = (curNode.Position - target.Position).Magnitude
		local sectionLength = (curNode.Position - nextNode.Position).Magnitude
		local newAlpha = ((distanceTraveledOnSection + curSpeed.Value * deltaTime)) / sectionLength
		
		--print(train, " -> Next Node:", nextNode, ", Current Front Node:", train.Locomotive.Front.Current.Value, ", Current Rear Node:", train.Locomotive.Back.Current.Value, ", Distance Traveled on Section:", distanceTraveledOnSection, ", Section Length:", sectionLength, ", New Alpha:", newAlpha)

		target.CFrame = curNode.CFrame:Lerp(nextNode.CFrame, newAlpha)

		if newAlpha >= 1 then
			NextNode(target)
		end
	end
	
	rs.Heartbeat:Connect(function(deltaTime)
		print(deltaTime)
		Move(front, deltaTime)
		Move(back, deltaTime)

		base.CFrame = CFrame.new(back.Position, front.Position) * CFrame.new(0,front.Size.Y/2 + base.Size.Y/2,-gap/2)
		loco_model:SetPrimaryPartCFrame(base.CFrame * CFrame.new(0,base.Size.Y/2 + loco_model.PrimaryPart.Size.Y/2,0))

		for i,v in pairs(train.Locomotive.Model.Sensor:GetTouchingParts()) do
			if v.Name == "Station" then
				if v:GetAttribute("StationName") ~= CurrentStation then
					CurrentStation = v:GetAttribute("StationName")
					print("Station trigger found", v)
					print(v)
					TweenService:Create(curSpeed, TweenInfo.new(2), {Value = 0}):Play()
					task.wait(1.5)
					local StationDoors = v.Doors.Value
					StationDoors.Door1.SoundPart.Move:Play()
					StationDoors.Door2.SoundPart.Move:Play()
					ClientTween.SendTween(train.Locomotive.Model.Door1.Door1, train.Locomotive.Model.Door1.Door1Opened.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(train.Locomotive.Model.Door1.Door2, train.Locomotive.Model.Door1.Door2Opened.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(train.Locomotive.Model.Door2.Door1, train.Locomotive.Model.Door2.Door1Opened.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(train.Locomotive.Model.Door2.Door2, train.Locomotive.Model.Door2.Door2Opened.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(StationDoors.Door1.Door1, StationDoors.Door1.Door1Opened.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(StationDoors.Door1.Door2, StationDoors.Door1.Door2Opened.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(StationDoors.Door2.Door1, StationDoors.Door2.Door1Opened.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(StationDoors.Door2.Door2, StationDoors.Door2.Door2Opened.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					task.wait(10)
					StationDoors.Door1.SoundPart.Move:Play()
					StationDoors.Door2.SoundPart.Move:Play()
					ClientTween.SendTween(train.Locomotive.Model.Door1.Door1, train.Locomotive.Model.Door1.Door1Closed.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(train.Locomotive.Model.Door1.Door2, train.Locomotive.Model.Door1.Door2Closed.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(train.Locomotive.Model.Door2.Door1, train.Locomotive.Model.Door2.Door1Closed.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(train.Locomotive.Model.Door2.Door2, train.Locomotive.Model.Door2.Door2Closed.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(StationDoors.Door1.Door1, StationDoors.Door1.Door1Closed.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(StationDoors.Door1.Door2, StationDoors.Door1.Door2Closed.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(StationDoors.Door2.Door1, StationDoors.Door2.Door1Closed.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					ClientTween.SendTween(StationDoors.Door2.Door2, StationDoors.Door2.Door2Closed.CFrame, {2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut})
					task.wait(3)
					TweenService:Create(curSpeed, TweenInfo.new(10), {Value = 30}):Play()
				end
			end
		end
	end) 
end

for i,v in pairs(script.Parent.Trains:GetChildren()) do
	InitializeTram(v)
end
  1. What solutions have you tried so far? I looked online, i tried fixing it myself.
2 Likes

Forgot to precise that my train use a node system that are parts named from Node_1 to Node_188

How many Parts in your train(s)?
Too many Parts can cause it to lag.
Also Touched instances could be causing lag as well. Make all the Parts of the train that don’t need to register a Touched instance CanTouch false.

Each train is made of 1027 parts

Wow, can you reduce that with MeshParts or possibly Unions?
Moving that many Parts is likely causing an issue.

Would unioning really make a difference?

Things I can say at first glance:

  • Comment out unnecessary print statements.
  • Replace (train.Locomotive.Model.Sensor:GetTouchingParts()) with workspace:GetPartsInPart(). You can supply an OverlapParams instance to filter the list based on CollisionGroups or a table, and thus you’ll need to check fewer parts.
  • Assuming a train can only be in one station at once, you can call break after you’ve detected that the train is inside a station.
  • You’re creating several tweens that are meant to run over the course of several seconds, several times a second. They’re overlapping with one-another.
  • Use the microprofiler.
  • There is no need to run the station check 60 times a second. As little as once a second would still feel responsive for a train game.

I must ask though, where did you get this code from? It seems… poorly thought out.

Should be replaced by simple math logic to determind if train is at a station or not.

local railroadStations  = {
Waterloo = Vector3.new(100, 0, 0),
London = Vector3.new(0, 0, 0),
}

if not isAtStation then
for name, position in railroadStations do
if (base.Position - position ).magnitude < 10 then
isAtStation = true
print("Welcome to", name)
openDoors()
break
end
end

then when you’re leaving, just set isAtStation = false after like 5 seconds.

Also:

  • Dont use pairs, it’s not nessecary nor faster.
  • Use variable names for EVERYTHING you reference more than once. Ex:
    local sensor = train.Locomotive.Model.Sensor
1 Like

Unions can be messier than MeshParts because they create more tris (faces) and they may get strange shading.
If you do then instead of calculating the movement of 1027 Parts you might be able (just guessing) to make that 10 or 20 Unions or MeshParts for better performance.
If you watch tutorials you could make the entire train car 1 MeshPart and create a Texture that gets all the details (signs, colors, panel lines, control panel without having to add additional Parts.