Setting Humanoid CFrame breaks movement

Repro steps:

  1. Download the repro file:
    rotatetest.rbxl (123.6 KB)
  2. Press F8 or the “Run” button in the dropdown of playtest options in the TEST ribbon bar tab
  3. Observe the NPC walking between two points
  4. Change the value of IntValue TestCase under the workspace to 2 or 3
  5. Observe the changes in behaviour

Expected behaviour:

The NPC moves between the green, glowing part and the spawnlocation, potentially facing different directions based on the value of TestCase, but should always be moving between all of them.

Actual behaviour:

When TestCase is 1 or 2, the NPC stutters along very slowly. In 1 they stutter while facing the camera, in 2 they stutter while slowly turning towards their moving direction. However, when TestCase is 3, they moving correctly without stuttering.

This is the code for that changes the NPC’s CFrame and shows the bug:

RunService.Heartbeat:Connect(function(Delta)
	
	if TestCase.Value == 1 then
		RootPart.CFrame = CFrame.new(RootPart.CFrame.Position)
		
	elseif TestCase.Value == 2 then
		RootPart.CFrame = CFrame.new(RootPart.CFrame.Position) * CFrame.Angles(RootPart.CFrame:ToOrientation())
		
	elseif TestCase.Value == 3 then
		RootPart.CFrame = RootPart.CFrame
	end
end)

Logically, TestCase values of 2 and 3 should behave identically, because they should represent the same CFrame. However, changing the CFrame in any way will cause the NPC to lose it’s velocity entirely. It seemingly tells the Humanoid to stop moving, the small jitters it makes are apparently because of the :MoveTo calls I have in a while loop. Making the time between calls longer makes the time between jitters longer, and removing the extra calls entirely prevents it from reaching it’s goal at all, after the first jitter it just stops forever.

Workaround: None
Date first experienced: There is a #help-and-feedback topic I found that dates back to July of 2021 of someone seemingly experiencing the same problem as detailed above:

Date last experienced: 24th July, 2023
Impact: High - I want to create bots for my first-person shooter, but they can’t aim while moving because of this bug.
Frequency: Always

5 Likes

Thanks for the report! We’ll follow up when we have an update for you.

5 Likes

This bug is still occurring in the latest Roblox version.

1 Like

When a script updates RootPart.CFrame to a new value, Humanoid always aborts the previous MoveTo command. It doesn’t look like this is mentioned in the documentation, but it is the expected behavior. I will make sure it’s added there.

In this example, case 1 sets a new value; and test case 2 too, likely due to float precision errors. Test case 3 uses the same value, so its a no-op.

There are two potential workarounds:

  1. Call MoveTo immediately after updating RootPart.CFrame.
    Keep in mind that, due to how replication works for these properties, clients will likely experience stuttery movement.

  2. (preferred) Use your own MoveTo implementation, and move the NPC with Humanoid:Move(). If you need to use it for path following, there is a good reference implementation that does exactly that in PlayerScripts. It’s in the ClickToMoveController module, and it’s called Pather.

3 Likes

Thank you for the response as well as providing a workaround. I had tried to use an immediate call to :MoveTo, but experienced the stutter then as well. I assumed that it was just :Move under-the-hood with some extra ease-of-use improvements, so I didn’t think to try :Move directly. Thanks again for giving me a workaround, I have been stuck with this issue in a lot of my projects for a while, and only recently thought to make a bug report.

2 Likes