Tween Train system

Greetings.

I am currently attempting to develop a system where a train will start and stop when using ‘resume’ and ‘pause’. The resume aspect of my script currently works. although the pause doesnt. Just asking for some feedback on whats going wrong. thanks!

local TweenService = game:GetService("TweenService")
local SecondsPerStud = 0.05

game.Players.PlayerAdded:Connect(function(player)
    player.Chatted:Connect(function(message)
	    if message:lower() == "pause" then
		    script.Parent.can.Value = false
	    end
	
	    if message:lower() == "resume" then
		    script.Parent.can.Value = true
	    end
    end)
end)


while true do
    wait()
    for _,v in pairs(workspace.Workspace.Transport.Track:GetChildren()) do
        local Info = TweenInfo.new(
	    	    (v.Position - script.Parent.Position).Magnitude * SecondsPerStud,
            Enum.EasingStyle.Linear
        )
        local tween = TweenService:Create(script.Parent, Info, {CFrame = v.CFrame})
	    if script.Parent.can.Value == false then
		    tween:Pause()
	    else
		    tween:Play()
		    tween.Completed:Wait()
	    end
    end	
end
3 Likes

Pausing won’t work because your tween is only local on the loop of each track.

I believe for you to solve this issue and for the function bettter is to use the Changed event rather than running a continous index loop to pause an already known tween.

2 Likes

I tried this it didnt work, You see doing that will just start it over again which I am currently looking to avoid.

Edit to my previous statement:

As before the changed event only lets the resume function work. not letting pause work.

1 Like

Would something like this work?

local tween = nil
for _,v in pairs(workspace.Workspace.Transport.Track:GetChildren()) do
        local Info = TweenInfo.new(
	    	    (v.Position - script.Parent.Position).Magnitude * SecondsPerStud,
            Enum.EasingStyle.Linear
        )
	    if script.Parent.can.Value == false then
		    if tween then tween:Pause() end
	    else
            tween = TweenService:Create(script.Parent, Info, {CFrame = v.CFrame})
		    tween:Play()
		    tween.Completed:Wait()
	    end
    end	
1 Like

It resumed, but again did not pause.

local TweenService = game:GetService("TweenService")
local SecondsPerStud = 0.05

game.Players.PlayerAdded:Connect(function(player)
    player.Chatted:Connect(function(message)
	    if message:lower() == "pause" then
		    script.Parent.can.Value = false
	    end
	
	    if message:lower() == "resume" then
			script.Parent.can.Value = true
	    end
    end)
end)

script.Parent.can.Changed:Connect(function()
	local tween = nil
	for _,v in pairs(workspace.Workspace.Transport.Track:GetChildren()) do
	    local Info = TweenInfo.new(
	    	    (v.Position - script.Parent.Position).Magnitude * SecondsPerStud,
	        Enum.EasingStyle.Linear
	    )
	    if script.Parent.can.Value == false then
		    if tween then tween:Pause() end
	    else
	        tween = TweenService:Create(script.Parent, Info, {CFrame = v.CFrame})
		    tween:Play()
		    tween.Completed:Wait()
	    end
	end		
end)

Hm, you might want to run some prints around to make sure “can” is false and is a bool, the position of v or “script.Parent” isn’t something crazy, pausing is being runned, and making sure that it goes past “tween.Completed:Wait()”

script.Parent.can.Changed:Connect(function()
	local tween = nil
	for _,v in pairs(workspace.Workspace.Transport.Track:GetChildren()) do
	    local Info = TweenInfo.new(
	    	    (v.Position - script.Parent.Position).Magnitude * SecondsPerStud,
	        Enum.EasingStyle.Linear
	    )
	    if script.Parent.can.Value == false then
		    if tween then tween:Pause(); print("Paused.") end
	    else
	        tween = TweenService:Create(script.Parent, Info, {CFrame = v.CFrame})
		    tween:Play()
		    tween.Completed:Wait()
			print("Resumed.")
	    end
	end		
end)

Neither print, although it does resume, so I am guessing that tween.Completed:Wait() might be stopping the print from running.

Although I do not know whats going on with the pause.

Just noticed I am setting nil everytime the value is changed, fixing and testing now.

Alright, I got the pause and resume working but not functioning as I intend.

When I pause it stops as I want it to, but then when I resume it goes back to the start of the track.

Do you know why?

local TweenService = game:GetService("TweenService")
local SecondsPerStud = 0.05

game.Players.PlayerAdded:Connect(function(player)
    player.Chatted:Connect(function(message)
	    if message:lower() == "pause" then
		    script.Parent.can.Value = false
	    end
	
	    if message:lower() == "resume" then
			script.Parent.can.Value = true
	    end
    end)
end)

local tween = nil

script.Parent.can.Changed:Connect(function()
	for _,v in pairs(workspace.Workspace.Transport.Track:GetChildren()) do
	    local Info = TweenInfo.new(
	    	    (v.Position - script.Parent.Position).Magnitude * SecondsPerStud,
	        Enum.EasingStyle.Linear
	    )
	    if script.Parent.can.Value == false then
		    if tween then tween:Pause(); print("Paused.") end
	    else
	        tween = TweenService:Create(script.Parent, Info, {CFrame = v.CFrame})
		    tween:Play()
		    tween.Completed:Wait()
			print("Resumed.")
	    end
	end		
end)

You’re using :GetChildren() so it’s gonna re-loop everything. You can use a dictionary and then clear it once it reaches the final track.

Yes, but then the same dilema will arise when setting the dictionary automatically from the track nodes. I will need to use getchildren to index the dictionary.

I’m not sure what you mean, I’m pretty sure you can do a simple dictionary check to make the track just not tween it again if it’s true. I have an example down below.

local tween = nil
local passedTracks = {}

script.Parent.can.Changed:Connect(function()
	for _,v in pairs(workspace.Workspace.Transport.Track:GetChildren()) do
	    local Info = TweenInfo.new(
	    	    (v.Position - script.Parent.Position).Magnitude * SecondsPerStud,
	        Enum.EasingStyle.Linear
	    )
	    if script.Parent.can.Value == false then
		    if tween then tween:Pause(); print("Paused.") end
	    elseif not passedTracks[v] then
	        tween = TweenService:Create(script.Parent, Info, {CFrame = v.CFrame})
		    tween:Play()
		    tween.Completed:Wait()
            passedTracks[v] = true
            if v == FINALTRACK then
               passedTracks = {}
            end
			print("Resumed.")
	    end
	end		
end)

Define the Tween information outside of the changed function, so you’re not redefining it each time you change the ‘Can’ value.

Define a ‘CurrentPoint’ outside of this changed function, which acts as the current node within the track that your train is aiming to move towards. Increment to the next point after the previous is reached. When you pause and resume again, your tween’s goal should be this CurrentPoint.

1 Like
local tween = nil
local current = nil

script.Parent.can.Changed:Connect(function()
	for _,v in pairs(workspace.Workspace.Transport.Track:GetChildren()) do
		current = v
	    local Info = TweenInfo.new(
	    	    (current.Position - script.Parent.Position).Magnitude * SecondsPerStud,
	        Enum.EasingStyle.Linear
	    )		
	    if script.Parent.can.Value == false then
		    if tween then tween:Pause(); print("Paused.") end
	    else
	        tween = TweenService:Create(script.Parent, Info, {CFrame = current.CFrame})
		    tween:Play()
		    tween.Completed:Wait()
			current = v
			print("Resumed.")
	    end
	end		
end)

Something like this?

Amending this: It still returns to the beginning of the track,

Not exactly what I had in mind, however I’ve made your code work quickly with the use of a BindableEvent. Do mind that when you pause it may not update immediately due to the fact that if a tween is already playing, it will have to wait until that tween has completed. And do note it isn’t the most effective way of doing such.

local tween = nil
local Can = true

local PauseEvent = Instance.new("BindableEvent")

script.Parent.can.Changed:Connect(function(State)
	Can = State
	if State then
		PauseEvent:Fire()
	end
end)


for _,v in pairs(workspace.Workspace.Transport.Track:GetChildren()) do
	local Info = TweenInfo.new((v.Position - script.Parent.Position).Magnitude * SecondsPerStud,Enum.EasingStyle.Linear)		
	if script.Parent.can.Value == false then
		if tween then 
			tween:Pause();
			print("Paused.") 
			PauseEvent.Event:Wait()
		end
	else
		tween = TweenService:Create(script.Parent, Info, {CFrame = v.CFrame})
		tween:Play()
		tween.Completed:Wait()
		print("Resumed.")
	end
end

Is there any way of doing it where I can make it pause anywhere during the track, my goal in this is to make a start stop button on a gui inwhich the driver can click.

By the way, that script with the bindablefunction doesnt work.

1 Like

Please try the code again as I’ve updated the loop to iterate through your original path for your Track, as I had changed it simply to workspace.Track:GetChildren() to experiment with it myself and forgot to change it back. It should definitely work.

However, I can’t say that I’ve ever done anything like this myself as I don’t believe I’ve seen a train system that can be driven by a player utilizing TweenService, as trains that use TweenService that I’ve seen typically are those with constant movement, such as in Jailbreak. I’m afraid I wouldn’t know too much about what you can do to achieve this, so I suggest researching trains that can be driven and seeing how they were done.

My suggestion if you still want to use this system with your GUI is to have a RemoteEvent that fires to the server, checks that the person who fired it is the driver, then pause the tween. Keep track of the current ‘node’ in the track that the train is moving towards and when the driver resumes again, create a new tween with the train’s current position and move it towards that node, before switching back to the other system where it moves from node to node. If this doesn’t work, unfortunately there isn’t much more help I can provide because this isn’t something I’ve done before.