Attach player to platform being moved by Cframes without welding them

If the title makes no sense let me explain. I have a moving platform that moves sideways, up and down. It works fine, however as I could not figure out hot to make it move the way I wanted it to with velocities I had to make it do so with CFrames. The issue with that is that unless I weld the player to the platform, the platform would just move without them. And so I had to weld them, but doing so locks the player in place. I want to somehow keep platform movement the way it currently is while also allowing the player to move while on the platform.

Help is appreciated

video: Download 2025-06-30 15-15-45.mp4 | LimeWire

code:

local Target = script.Parent.Target
local Touching = script.Parent.Touching

local UpPart = game.Workspace.UpPart
local DownPart = game.Workspace.DownPart

local UpButton = script.Parent.Buttons.Up.ClickDetector
local DownButton = script.Parent.Buttons.Down.ClickDetector

local masterIndex = 0

UpButton.MouseClick:Connect(function()
	Target.Value = "Up"
end)

DownButton.MouseClick:Connect(function()
	Target.Value = "Down"
end)

local createdWelds = {}

function cleanWelds()
	for i, weld in pairs(createdWelds) do
		i.Parent.Humanoid.PlatformStand = false
		weld:Destroy()
	end
end


function movePlatform()
	
	cleanWelds()
	
	local touchingPlayers = workspace:GetPartsInPart(Touching)
	
	for i, potentialPlayer in pairs(touchingPlayers) do
		
		if potentialPlayer.Name ~= "HumanoidRootPart" then continue end
		local HRP = potentialPlayer
		
		
		HRP.Parent.Humanoid.PlatformStand = true
		local newWeld = Instance.new("WeldConstraint", HRP)
		newWeld.Part0 = script.Parent.Platform
		newWeld.Part1 = HRP
		
		createdWelds[HRP] = newWeld
	end
	
	masterIndex += 1
	local indexSave = masterIndex
	
	local increment = (Target.Value == "Up" and 0.2 or -0.2)
	local targetPart = (Target.Value == "Up" and UpPart or DownPart)

	repeat
		local formulatedCF = script.Parent.PrimaryPart.CFrame + script.Parent.PrimaryPart.CFrame.LookVector * increment
		task.wait(0.01)
		script.Parent:PivotTo(formulatedCF)
	until (targetPart.Position - script.Parent.PrimaryPart.Position).Magnitude <= 0.1 or indexSave ~= masterIndex
	
	if indexSave == masterIndex then
		cleanWelds()
	end
	
end

Target:GetPropertyChangedSignal("Value"):Connect(movePlatform)
1 Like

You would have to raycast in the direction that your character is actually standing on the platform while it is moving. Then replace the platform’s old CFrame with the platform’s new CFrame and change the CFrame of the humanoid root part as the platform changes direction. The main reason why the humanoid root part’s CFrame is relatively invertible opposed to the platform is because you will be stationary, whereas the platform would still be moving at some extent. So, to prevent this your humanoid root part’s cframe would change with the moving platform’s cframe.

local rs = game:GetService("RunService")
local players = game:GetService("Players")
local lastPlatformCFrame = nil
players.PlayerAdded:Connect(function(player)
local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:FindFirstChild("HumanoidRootPart")
rs.RenderStepped:Connect(function()
local rayparams = RaycastParams.new()
rayparams.FilterDescendantsInstances = {character}
rayparams.FilterType = Enum.RaycastFilterType.Exclude
--not sure but either of these two would work
--local raycast = workspace:Raycast(rootPart.CFrame, rootPart.CFrame:PointToWorldSpace(Vector3.new(0,0,-50)), rayparams)
--local raycast = workspace:Raycast(rootPart.CFrame, rootPart.CFrame:VectorToWorldSpace(Vector3.new(0,0,-50)), rayparams)
if raycast and raycast.Instance.Name == "Platform" then --platform name?
local platformCFrame = raycast.Instance.CFrame
if lastPlatformCFrame then
local cf = platformCFrame * lastPlatformCFrame:Inverse()
hrp.CFrame = cf * hrp.CFrame
end
lastPlatformCFrame = platformCFrame
else
lastPlatformCFrame = nil
end
end)
end)

Unless you are looking for linearVelocity or vectorForce, tweening/lerping can also constantly change the CFrame values of a platform would work as well.

I can modify this to something that can be applicable to platform movement upon clicking direction arrows.

Did you want to use tween service? Honestly I can make it for you because its really easy ngl

local Target = script.Parent.Target
local Touching = script.Parent.Touching

local UpPart = game.Workspace.UpPart
local DownPart = game.Workspace.DownPart

local UpButton = script.Parent.Buttons.Up.ClickDetector
local DownButton = script.Parent.Buttons.Down.ClickDetector

local masterIndex = 0

UpButton.MouseClick:Connect(function()
	Target.Value = "Up"
end)

DownButton.MouseClick:Connect(function()
	Target.Value = "Down"
end)

local createdWelds = {}

function cleanWelds()
	for i, weld in pairs(createdWelds) do
		i.Parent.Humanoid.PlatformStand = false
		weld:Destroy()
	end
end


function movePlatform()
	
	cleanWelds()
	
	local touchingPlayers = workspace:GetPartsInPart(Touching)
	
	for i, potentialPlayer in pairs(touchingPlayers) do
		
		if potentialPlayer.Name ~= "HumanoidRootPart" then continue end
		local HRP = potentialPlayer
		
		
		--HRP.Parent.Humanoid.PlatformStand = true
		local newWeld = Instance.new("WeldConstraint", HRP)
		--newWeld.Part0 = script.Parent.Platform
		--newWeld.Part1 = HRP
		
		createdWelds[HRP] = newWeld
	end
	
	masterIndex += 1
	local indexSave = masterIndex
	
	local increment = (Target.Value == "Up" and 0.2 or -0.2)
	local targetPart = (Target.Value == "Up" and UpPart or DownPart)

	repeat
		local formulatedCF = script.Parent.PrimaryPart.CFrame + script.Parent.PrimaryPart.CFrame.LookVector * increment
		
		for i, root in pairs(createdWelds) do
			i.CFrame = i.CFrame + script.Parent.PrimaryPart.CFrame.LookVector * increment
		end
		
		task.wait(0.01)
		script.Parent:PivotTo(formulatedCF)
	until (targetPart.Position - script.Parent.PrimaryPart.Position).Magnitude <= 0.1 or indexSave ~= masterIndex
	
	if indexSave == masterIndex then
		cleanWelds()
	end
	
end

Target:GetPropertyChangedSignal("Value"):Connect(movePlatform)

Tried that, the player does move with the platform now however as the loop that moves it is so quick the player is just rotates around wildly and isn’t able to actually move anywhere on the platform

I could do that for the platform but if i tween the root part it would also take away control from the character

That was probably because I never used welds to keep the player stuck to the platform and every render changed the player’s CFrame while the platform below the root part was being raycasted (mistakenly made the character rotate at unpredictable speeds). It was designed to be a whole separate script without the use of constraints.

Assuming that you want to keep the majority of your code, perhaps tweening the part’s CFrame works too, just multiply the CFrame of the humanoid root part by its inverse after tweening a part’s CFrame from one place to another.

There is this popular thread about this issue

I also have a reply of my own that does basically the same thing (although it applies to any object. Should probably exclude unanchored ones though)

The basic idea is get by how much the object (on which the player is standing) moved since the previous frame, and offset the character’s position by it. It can be done with vector math, or with CFrame math (which will apply rotation as well)

This is also what @Jayigon’s solution does, but specific to your code and the platform (although I can’t say if it is bug free)

What a coincidence, I was about to send this exact link just before you replied. I definitely never came up with the idea of the hrp.CFrame = rel * CFrame:Inverse() and all the credit goes to this post regarding the jailbreak train system.

1 Like

I am assuming that you prevent the usage of welds, so that players may move freely or jumping around the platform. I do not know how to create a script that allows enough momentum when jumping on a moving object, but this post may provide more information:

BendsSpace in this thread. Seems to have a nice system for this.

I did what what suggested in the jailbreak thread and it works perfectly.

Local script in starterCharacterScripts:

local plr = game.Players.LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local root = char.HumanoidRootPart

local RaycastParam = RaycastParams.new()
RaycastParam.FilterType = Enum.RaycastFilterType.Exclude
RaycastParam.FilterDescendantsInstances = {char}

local platform =  game.Workspace.MovingPlatform.Platform

local RS = game:GetService("RunService")


local lastCFrame = nil

function update()
	local rayResult = workspace:Raycast(root.Position, Vector3.new(0,-50,0), RaycastParam)

	if rayResult.Instance.Name == "Platform" then
		if lastCFrame == nil then
			lastCFrame = rayResult.Instance.CFrame
		end

		local rel = rayResult.Instance.CFrame * lastCFrame:inverse()
		lastCFrame = rayResult.Instance.CFrame	

		root.CFrame = rel * root.CFrame		
	end

end

game["Run Service"]:BindToRenderStep("Update", 1, update)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.