OK, I found the issue and rewrote the basic code you posted with a fix and some of the stuff I previously said.
-- Localscript inside of StarterCharacterScripts
local Players = game:GetService("Players")
local player = game.Players.LocalPlayer
local RunService = game:GetService('RunService')
local LastHit
local LastTrainCFrame
local Function
local Function2
local Whitelist = {workspace.Trains}
Function = RunService.Heartbeat:Connect(function()
local RootPart = player.Character.HumanoidRootPart
local Ignore = player.Character
local ray = Ray.new(RootPart.CFrame.p, Vector3.new(0,-500,0))
local Hit, Position, Normal, Material
if LastHit then
Hit, Position, Normal, Material = workspace:FindPartOnRayWithWhitelist(ray,{LastHit})
if not Hit then
Hit, Position, Normal, Material = workspace:FindPartOnRayWithWhitelist(ray,Whitelist)
end
else
Hit, Position, Normal, Material = workspace:FindPartOnRayWithWhitelist(ray,Whitelist)
end
if Hit then
local Train = Hit
if not LastTrainCFrame then
LastTrainCFrame = Train.CFrame
end
local TrainCF
if Train ~= LastHit and LastHit then
TrainCF = LastHit.CFrame
else
TrainCF = Train.CFrame
end
local Rel = TrainCF * LastTrainCFrame:inverse()
LastTrainCFrame = Train.CFrame
RootPart.CFrame = Rel * RootPart.CFrame
LastHit = Train
if Train ~= LastHit then
LastTrainCFrame = Train.CFrame
end
else
LastTrainCFrame = nil
end
if not Function2 then
Function2 = player.Character.Humanoid.Died:Connect(function()
Function:Disconnect()
Function2:Disconnect()
end)
end
end)
The issue was that when you switched parts, you’d set the LastTrainCFrame to the new part’s CFrame, making the Rel value equal to an identity matrix (no offset and no orientation), thus it would not move the player forward, allowing physics to push them back.
Also, decrease the -500 to something like -10 as it can cause lag on complex maps.