Got it working!! WOOO
I ended up going back to CFrame but using your suggestion of rendering the collisions only on the client. Then I used this code to sync momentum:
--BrahGames 07-27-2021 rewritten https://devforum.roblox.com/t/jailbreak-train-platform-system/236339
--slightly edited to work with bodymovers and such /maxx
local Players = game:GetService("Players")
local player = game.Players.LocalPlayer
local RunService = game:GetService('RunService')
local CheckStates = true
local LastPartCFrame
local LastPart
local Function
local Function2
function CFrameLogic(Raycast, CFramePart)
--------------------------------------------------------------- MOVE PLAYER TO NEW POSITON FROM OLD POSITION
if Raycast.Instance ~= LastPart then
LastPart = Raycast.Instance
LastPartCFrame = nil
end
local Train = Raycast.Instance
if LastPartCFrame == nil then -- If no LastTrainCFrame exists, make one!
LastPartCFrame = Train.CFrame -- This is updated later.
end
local TrainCF = Train.CFrame
local Rel = TrainCF * LastPartCFrame:inverse()
LastPartCFrame = Train.CFrame -- Updated here.
CFramePart.CFrame = Rel * CFramePart.CFrame -- Set the player's CFrame
--print("set")
end
Function = RunService.Heartbeat:Connect(function()
--------------------------------------------------------------- CHECK PLATFORM BELOW
-- Build a "RaycastParams" object
local HumanoidRootPart = player.Character.HumanoidRootPart
local humanoid = player.Character.Humanoid
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Include
raycastParams.FilterDescendantsInstances = {game:GetService("CollectionService"):GetTagged("BoatCollisions")}
raycastParams.IgnoreWater = true
raycastParams.CollisionGroup = "Default"
-- Cast the ray
local Hit = workspace:Raycast(HumanoidRootPart.CFrame.p, Vector3.new(0,-15,0), raycastParams) --Casts a ray facing down
--------------------------------------------------------------------------------------------
if Hit then -- Check if the ray hits and if it hits an object inside of MovingObjects
if CheckStates then -- check if the character is jumping
local currentState = humanoid:GetState()
--if currentState == Enum.HumanoidStateType.Freefall or currentState == Enum.HumanoidStateType.Jumping or currentState == Enum.HumanoidStateType.FallingDown then
CFrameLogic(Hit, HumanoidRootPart)
--else
--LastPartCFrame = nil -- Clear the value when the player gets off.
--end
else
CFrameLogic(Hit, HumanoidRootPart)
end
else
LastPartCFrame = nil -- Clear the value when the player gets off.
end
end)
Function2 = player.Character.Humanoid.Died:Connect(function()
Function:Disconnect() -- Stop memory leaks
Function2:Disconnect() -- Stop memory leaks
end)
It’s a slightly modified version of the code from this post.
Thanks so much for your help @BendsSpace.