What is the most effective way to replicate Motor6D.Transform?

Hey DevForum

I’m looking for a solution to my problem. Correct me if I’m wrong, but Motor6D.Transform is essentially the superior replacement to manually changing the C0 of a Motor6D, because setting the C0 or C1 messes up animations.

I’m currently working on a way to use Motor6D.Transform to move the arms of the characters to face forward even if the torso is angled down.

However, the main issue I found is replicating it. Because I used to be able to just set the C0 for a permanent offset (which I can’t do anymore), I now have to somehow let the character’s Motor6D.Transform property be updated every frame with the right values WHILE having to replicate to the other clients.

This issue has honestly got me completely stumped and essentially killed all motivation for my game, does anyone know a way to get around this?

replicatedstorage.Events.TorsoMovement.OnClientEvent:Connect(function(character, cameraY)
	--if not character or character.PrimaryPart then return end
	local plrWaist = character.UpperTorso.Waist
	local plrNeck = character.Head.Neck
	
	local neckTransformOffset = CFrame.fromEulerAnglesYXZ(0, 0, 0)
	local waistTransformOffset = CFrame.fromEulerAnglesYXZ(0, 0, 0)
	
	local YTorso = cameraY
	
	local renderStepped = rs.RenderStepped:Connect(function()
		if not character:GetAttribute("DisableLook") then
			neckTransformOffset = CFrame.fromEulerAnglesYXZ(cameraY*0, 0, 0);
		end

		if character:GetAttribute("DisableTorso") and YTorso > 0 or not character:GetAttribute("DisableTorso") then
			waistTransformOffset = CFrame.fromEulerAnglesYXZ(YTorso*1, 0, 0);
		elseif character:GetAttribute("DisableTorso") and YTorso <= 0 then
			waistTransformOffset = CFrame.fromEulerAnglesYXZ(0, 0, 0)
		end
	end)

	local stepped = rs.Stepped:Connect(function()
		if plrNeck then
			plrNeck.Transform = neckTransformOffset
		end
		if plrWaist then
			plrWaist.Transform = waistTransformOffset
		end
	end)
	
	print("connected events")
	
	coroutine.wrap(function()
		wait(0.15)
		print("dc'd "..character.Name)
		stepped:Disconnect()
		renderStepped:Disconnect()
	end)()
end)

I thought Animators existed for this? I haven’t touched animation in a long time so I could be wrong.

There are already Animators in the characters, but this doesn’t replicate the movements. I’ve posted the code I’ve made to try it, but it doesn’t work and makes the character’s Motor6D.Transform glitch back to the default for a frame and then go back to what it is supposed to be.

This is very similar to replicating a player’s look direction. An example was created by okeanskiy: Rotate Head to Camera Direction! - Roblox (Motor6D CFrame and Trigonometry) - YouTube

To sum it up: send the angles to the server, filter the incoming angles on the server, then send them to all of the clients except the person who sent it. When the client receives a cframe, tween the player’s joint to match that cframe.

I wish that would work, but manually setting the C0 now breaks animations.

I’ve tinkered with the code to make this, but the character twitching is still there.

local characterEventsTable = {}

replicatedstorage.Events.TorsoMovement.OnClientEvent:Connect(function(character, cameraY)
	--if not character or character.PrimaryPart then return end
	
	local plrWaist = character.UpperTorso.Waist
	local plrNeck = character.Head.Neck
	
	local neckTransformOffset = CFrame.fromEulerAnglesYXZ(0, 0, 0)
	local waistTransformOffset = CFrame.fromEulerAnglesYXZ(0, 0, 0)
	
	local YTorso = cameraY
	
	local renderStepped = rs.RenderStepped:Connect(function()
		if not character:GetAttribute("DisableLook") then
			neckTransformOffset = CFrame.fromEulerAnglesYXZ(cameraY*0, 0, 0);
		end

		if character:GetAttribute("DisableTorso") and YTorso > 0 or not character:GetAttribute("DisableTorso") then
			waistTransformOffset = CFrame.fromEulerAnglesYXZ(YTorso*1, 0, 0);
		elseif character:GetAttribute("DisableTorso") and YTorso <= 0 then
			waistTransformOffset = CFrame.fromEulerAnglesYXZ(0, 0, 0)
		end
	end)

	local stepped = rs.Stepped:Connect(function()
		if plrNeck then
			plrNeck.Transform = neckTransformOffset
		end
		if plrWaist then
			plrWaist.Transform = waistTransformOffset
		end
	end)
	
	if characterEventsTable[character.Name.."Events"] then
		for i, event in pairs(characterEventsTable[character.Name.."Events"]) do
			event:Disconnect()
		end
	else
		characterEventsTable[character.Name.."Events"] = {}
	end
	
	table.insert(characterEventsTable[character.Name.."Events"], renderStepped)
	table.insert(characterEventsTable[character.Name.."Events"], stepped)
	
end)

Something like this in the video below?

BTW I think what you are doing is overwriting the .Transform animations, maybe consider *= to add in animations perhaps.

	local stepped = rs.Stepped:Connect(function()
		if plrNeck then
			plrNeck.Transform = plrNeck.Transform * neckTransformOffset
		end

You can even experiment with swapping the order, just a tip or thing you can do if you find something off with your CFrame.

	local stepped = rs.Stepped:Connect(function()
		if plrNeck then
			plrNeck.Transform =  neckTransformOffset*plrNeck.Transform
		end

I think the issue here is that trying to rewrite the events makes the character glitch back to the default Motor6D.Transform property for a moment, then go back to what it is supposed to be when the code connects a new event to the Transform property.

This is the code I have now, it mostly works but could definitely be improved. I have no idea how to go about this.

replicatedstorage.Events.TorsoMovement.OnClientEvent:Connect(function(character, cameraY)
	--if not character or character.PrimaryPart then return end
	
	local plrWaist = character.UpperTorso.Waist
	local plrNeck = character.Head.Neck
	
	local neckTransformOffset = CFrame.fromEulerAnglesYXZ(0, 0, 0)
	local waistTransformOffset = CFrame.fromEulerAnglesYXZ(0, 0, 0)
	
	local YTorso = cameraY
	
	print(torsoMovementCounter)
	
	characterEventsTable[character.Name.."Events"..torsoMovementCounter] = {}
	
	local renderStepped = rs.RenderStepped:Connect(function()
		if not character:GetAttribute("DisableLook") then
			neckTransformOffset = CFrame.fromEulerAnglesYXZ(0, 0, 0);
		end
		
		if character:GetAttribute("DisableTorso") and YTorso > 0 or not character:GetAttribute("DisableTorso") then
			waistTransformOffset = CFrame.fromEulerAnglesYXZ(YTorso*1, 0, 0);
		elseif character:GetAttribute("DisableTorso") and YTorso <= 0 then
			waistTransformOffset = CFrame.fromEulerAnglesYXZ(0, 0, 0)
		end
	end)

	local stepped = rs.Stepped:Connect(function()
		if plrNeck then
			plrNeck.Transform = neckTransformOffset
		end
		if plrWaist then
			plrWaist.Transform = waistTransformOffset
		end
	end)
	
	local ogCounter = torsoMovementCounter
	
	characterEventsTable[character.Name.."Events"..torsoMovementCounter][torsoMovementCounter] = renderStepped
	characterEventsTable[character.Name.."Events"..torsoMovementCounter][torsoMovementCounter.."Stepped"] = stepped
	
	delay(0.2, function() -- disconnect old events.
		characterEventsTable[character.Name.."Events"..ogCounter][ogCounter]:Disconnect()
		characterEventsTable[character.Name.."Events"..ogCounter][ogCounter.."Stepped"]:Disconnect()
	end)
	
	torsoMovementCounter += 1
end)