Trouble with turning HumanoidRootPart with camera

I’m making a flight script, and part of it includes changing the player’s HumanoidRootPart CFrame to match the camera’s rotation, so that the player is always facing the same direction of the camera while in flight. However, the solution I am using appears to be very “jittery” when facing specific directions.


I’m not entirely sure what is causing this, although I think it has something to do with the camera using the root part’s position as a center?

runService.RenderStepped:Connect(function()
	tweenedvectorvalue = tweenedvectorvalue:Lerp(vectorvalue, 0.075)
	if flying then
		rootpart.CFrame = CFrame.new(rootpart.Position)*CFrame.fromEulerAnglesYXZ(camera.CFrame.Rotation:ToEulerAnglesYXZ())
	end
	local velocityvalue = camera.CFrame.Rotation*tweenedvectorvalue
	print(vectorvalue)
	velocity.VectorVelocity = velocityvalue
end)

I’m not very experienced with CFrames, so if anyone knows what is causing this or knows a better way to handle this, please let me know!

1 Like

The reason it’s jittering is because you are using shift lock, which is part of the default PlayerScriptsLoader in StarterPlayerScripts. (Which only shows when you play test the game)

Shift lock will lock the camera position to the right side on the X axis of your character’s head.
Because of this, your code will constantly be fighting with the default PlayerScriptsLoader script which is why you constantly see jittering.

You need to create a custom camera script in order to achieve a similar effect which would be very difficult to do since you need to manually code the whole thing from the ground up (i.e. getting the mouse movement to actually move the camera)

An easier way to do this would be to create a local script that gets access to the MouseLockController module (which is used by the default PlayerScriptsLoader script that I mentioned) which can be located under
StarterPlayerScripts>PlayerModule>CameraModule>MouseLockController.

From there you can change the CAMERA_OFFSET_DEFAULT to Vector3.new(0,0,0)

-- this one at line 15 (should be at line 15, if its not then find using CTRL+F)
local CAMERA_OFFSET_DEFAULT = Vector3.new(1.75,0,0)  

Then you can just adjust it from there so that you can change the position of the camera to the center of the players head or however you like.

Here is the full guide on how to do this:

First create a local script (or you can use the same local script that you are using for the camera movement)

then add in this code:

local LocalPlayer = game:GetService("Players").LocalPlayer
local MouseLockController = require(LocalPlayer.PlayerScripts.PlayerModule.CameraModule.MouseLockController);

MouseLockController.CAMERA_OFFSET_DEFAULT = Vector3.new(0,0,0); -- you can adjust this however you like

Please take note that I am not entirely sure if this is the one that uses camera Shift Lock. But if it does work and nothing is wrong then let me know :smiling_face_with_three_hearts:

So far I have tried two things, which are editing the offset with a local script and changing the offset by pasting the module script in StarterPlayerScripts and editing line 15 to be 0,0,0. I couldn’t get the local script to change the offset, and while editing the module script manually did change the offset, it still didn’t fix the jittering.


Also, I’m not sure why shift lock would be causing the problem even when shift lock isn’t active (although I don’t know how the default camera system works.)

Oh sorry, I thought you were using shift lock. I apologise for that :pray:

I tried running your code and got the same results

The solution is actually quite simple which I missed.
try running this code

RunService.PreRender:Connect(function()
	playerHRP.CFrame = CFrame.new(playerHRP.Position)*camera.CFrame.Rotation
end)

This still doesn’t seem to be fixing my issue, (although it did point out that the ToEulerAngles stuff was unnecessary, thank you!) and I’m not sure what’s causing it at this point. The jitteriness makes me think the default camera system is affecting the humanoid root part position because of my code, and then that is affecting the camera position because of how the camera centers, causing some kind of loop. However, I have no idea if this is true, or how I would go about fixing it (other than rewriting the camera system, which would be a lot of work.)

This is my current code that affects the camera rotation.

runService.PreRender:Connect(function()
	if flying then
		rootpart.CFrame = CFrame.new(rootpart.Position)*camera.CFrame.Rotation
	end
end)

These are the results it gives me


One more thing to note is that the jitteriness seems to increase the more vertical the camera angle is.

That is very strange indeed. It seems like the root cause could probably be elsewhere in the code because according to my testing it worked perfectly fine for me

Here is the code for the test incase you want to try it out yourself(I’m not sure if this could be an issue on the studio version because a new update rolled out a few days ago that caused me to update roblox studio but just test it if it works):

-- fly bug recreation

local RunService=game:GetService("RunService");

local player = game:GetService("Players").LocalPlayer
local Character = player.Character or player.CharacterAdded:Wait()
local playerHRP = Character:WaitForChild("HumanoidRootPart");

-- lock the player and set player 10 studs above the ground to simulate flight
playerHRP.Anchored = true;
player.Character.Humanoid.PlatformStand = true
local camera = workspace.CurrentCamera;
playerHRP.CFrame = CFrame.new(0,10,0);

RunService.PreRender:Connect(function()
	playerHRP.CFrame = CFrame.new(playerHRP.Position)*camera.CFrame.Rotation
end)

I will try to come up with a solution but for now its quite difficult to replicate the bug you have currently

1 Like

Hi! I might’ve found what’s causing this.
I’m currently using flight by changing the LinearVelocity of the HumanoidRootPart, which means the part has to be unanchored.
Turning off the linear velocity (seems) to fix the problem (as well as anchoring the HumanoidRootPart), although it’s hard to tell because my character is flopping on the ground while it happens.
In which case, is there a better way to handle flight, or a way to get them to not interfere?

So I was experimenting with Linear Velocity, and it appears that whenever you are close to the ground/part the calculation seems to bug out and it violently shakes the character. Not too sure why this happens. From what I can tell, changing the parameters of MaxAxesForce somewhat affects the bug. I will upload my version of the code as well as a video shortly after doing more thorough testing.

I have found the solution, and it’s actually to not use the humanoid’s CFrame. The reason why updating the CFrame causes the bug is because its constantly realigning itself and switching back to the standing position(Which is why you constantly see it jittering when the character is pointed downwards and becomes normal happens when you make it straight). It will also mess with the physics calculation when you are too close to the ground/part (Not sure why) so we definitely have to scrap the idea of updating the character’s CFrame directly and find a smoother and safer approach.

The solution is to combine it with Align Orientation.
What Align Orientation does is it will calculate the rotation of the first part and slowly(or quickly depending on how fast you want it to be) align it to the second part. This is way better because it will actually rotate the character instead of simply CFraming it every frame as we did before.

Here is the full guide:

-- Player related
local player = game:GetService("Players").LocalPlayer
local playerCharacter = player.Character or player.CharacterAdded:Wait();
local playerHRP = playerCharacter:WaitForChild("HumanoidRootPart");
local playerAnimations:boolean = playerCharacter.Animate.Disabled;

-- Instantiating the Align Orientation
local camera = workspace.CurrentCamera;
local trackCamera = Instance.new("Part", game.Workspace); -- create a part that uses the camera CFrame (more explanation outside this code in the comment below)
local cameraAttachment = Instance.new("Attachment", trackCamera);
local alignOrientation = Instance.new("AlignOrientation", playerHRP);

-- Default settings for the Align Orientation
trackCamera.CanCollide = false; -- make sure the camera is not colliding with this
trackCamera.Transparency = 1; -- make sure the camera can't see the part
alignOrientation.Attachment1 = cameraAttachment; -- Set the orientation of the part you want it to track
alignOrientation.RigidityEnabled = true; -- This its for instant rotation

RunService.PreRender:Connect(function()
	trackCamera.CFrame = camera.CFrame; -- This is important! We need the rotation of the camera and setting it to the part
	if flying == true then
		alignOrientation.Attachment0 = playerHRP.RootAttachment; -- Connects the Align Orientation so it can start rotating the character
		fly(); -- your fly code
	end
	if flying == false then
		alignOrientation.Attachment0 = nil; - Disconnects the Align Orientation so you can move freely
	end
end)

First we create a Part that tracks the current camera. The reason we are doing this instead of directly using the camera is because adding attachments to the camera is illegal in Roblox Studio (Not sure why lol). So to counter this, we just create a part with the attachment and copy the camera’s CFrame to the part so that it can be used for the player’s rotation.

Then we add the default settings. These do not need to be updated at any given point but is necessary for this whole thing to work. Doing this will make sure the character actually rotates at the desired orientation

Lastly in the RunService, in order to connect the rotation of the camera to the character we just simply connect the Attachment0 from the align orientation when fly is turned on and disconnecting it when fly is turned off

Here is a video demo

Here is the whole code:

--!native
--!strict

-- Includes
local UIS = game:GetService("UserInputService");
local RunService=game:GetService("RunService");

-- Player related
local player = game:GetService("Players").LocalPlayer
local playerCharacter = player.Character or player.CharacterAdded:Wait();
local playerHRP = playerCharacter:WaitForChild("HumanoidRootPart");
local playerAnimations:boolean = playerCharacter.Animate.Disabled;
local camera = workspace.CurrentCamera;

-- Fly related
local trackCamera = Instance.new("Part", game.Workspace);
trackCamera.CanCollide = false;
trackCamera.Transparency = 1;
local cameraAttachment = Instance.new("Attachment", trackCamera);
local linearVelocity = Instance.new("LinearVelocity", playerHRP);
linearVelocity.ForceLimitMode = Enum.ForceLimitMode.Magnitude;
linearVelocity.VectorVelocity = Vector3.new(0,0,0);
linearVelocity.MaxForce = 100000;
local alignOrientation = Instance.new("AlignOrientation", playerHRP);
alignOrientation.Attachment1 = cameraAttachment;
alignOrientation.RigidityEnabled = true;
local flying = false;
local keyPressed = {
	W = false,
	S = false,
	A = false,
	D = false
};

UIS.InputBegan:Connect(function(key, GPE)
	if GPE then return; end
	if key.KeyCode == Enum.KeyCode.W then keyPressed.W = true; end
	if key.KeyCode == Enum.KeyCode.A then keyPressed.A = true; end
	if key.KeyCode == Enum.KeyCode.S then keyPressed.S = true; end
	if key.KeyCode == Enum.KeyCode.D then keyPressed.D = true; end
	if key.KeyCode == Enum.KeyCode.Space then
		if flying then
			flying = not flying;
			playerAnimations = not playerAnimations;
		else
			linearVelocity.Attachment0 = playerHRP.RootAttachment;
			alignOrientation.Attachment0 = playerHRP.RootAttachment;
			flying = true;
		end
	end
end)

UIS.InputEnded:Connect(function(key, GPE)
	if key.KeyCode == Enum.KeyCode.W then keyPressed.W = false; end
	if key.KeyCode == Enum.KeyCode.A then keyPressed.A = false; end
	if key.KeyCode == Enum.KeyCode.S then keyPressed.S = false; end
	if key.KeyCode == Enum.KeyCode.D then keyPressed.D = false; end
end)

local function flyDirection()
	--playerHRP.CFrame = CFrame.new(playerHRP.Position) * camera.CFrame.Rotation;
	playerHRP.LinearVelocity.VectorVelocity = Vector3.new(0,0,0);
	if keyPressed.W then playerHRP.LinearVelocity.VectorVelocity = camera.CFrame.LookVector * 50; end
	if keyPressed.A then playerHRP.LinearVelocity.VectorVelocity = camera.CFrame.RightVector * -50; end
	if keyPressed.S then playerHRP.LinearVelocity.VectorVelocity = camera.CFrame.LookVector * -50; end
	if keyPressed.D then playerHRP.LinearVelocity.VectorVelocity = camera.CFrame.RightVector * 50; end
end

local function playerFly()
	if flying == true then
		flyDirection();
	end
	if flying == false then
		linearVelocity.Attachment0 = nil;
		alignOrientation.Attachment0 = nil;
	end
end

RunService.PreRender:Connect(function()
	trackCamera.CFrame = camera.CFrame;
	playerFly();
end)

Let me know if this worked for you or not. If it did, be sure to mark this as solution so that others can find this post! :smiling_face_with_three_hearts: :partying_face:

Have you tried setting the humanoid state type to physics?

Humanoid:ChangeState(Enum.HumanoidStateType.Physics)

1 Like

Hey, I tried testing out the code you mentioned and it didn’t seem to work. However, I noticed another property called AutoRotate (I’m guessing this is the one that resets the character orientation to default) and set it to false

Humanoid.AutoRotate = false

I tested the code you mentioned and the one I found in the property tab but both of them still have the same problems:

This is the portion of code that was used to run it in case its hard to see in the video

UIS.InputBegan:Connect(function(key, GPE)
	if GPE then return; end
	if key.KeyCode == Enum.KeyCode.W then keyPressed.W = true; end
	if key.KeyCode == Enum.KeyCode.A then keyPressed.A = true; end
	if key.KeyCode == Enum.KeyCode.S then keyPressed.S = true; end
	if key.KeyCode == Enum.KeyCode.D then keyPressed.D = true; end
	if key.KeyCode == Enum.KeyCode.Space then
		if flying then
			flying = not flying;
			playerAnimations = not playerAnimations;
			
			-- There isn't an Idle state for this one so I just used Landed as a substitute 
			playerCharacter.Humanoid:ChangeState(Enum.HumanoidStateType.Landed)
		else -- if not flying anymore then disable everything else
			linearVelocity.Attachment0 = playerHRP.RootAttachment;
			flying = true;
			
			-- Testing if changing the state to physics works:
			playerCharacter.Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
			
			-- For second testing
			--playerCharacter.Humanoid.AutoRotate = false; -- AutoRotate
		end
	end
end)

Using AutoRotate on the other hand is much more worse for some strange reason:

I would like to mention that all of this only happens when the character is near/touches the ground/part. Using the Align Orientation is probably the best fix for now.

Worth a try though. Thanks for your input as well :smiling_face_with_three_hearts:

Hey i didn’t notice you already solved the question but i just wanted to add something

local function flyDirection()
	--playerHRP.CFrame = CFrame.new(playerHRP.Position) * camera.CFrame.Rotation;
	playerHRP.LinearVelocity.VectorVelocity = Vector3.new(0,0,0);
	if keyPressed.W then playerHRP.LinearVelocity.VectorVelocity += camera.CFrame.LookVector * 50; end
	if keyPressed.A then playerHRP.LinearVelocity.VectorVelocity += camera.CFrame.RightVector * -50; end
	if keyPressed.S then playerHRP.LinearVelocity.VectorVelocity += camera.CFrame.LookVector * -50; end
	if keyPressed.D then playerHRP.LinearVelocity.VectorVelocity += camera.CFrame.RightVector * 50; end
end

I noticed you can only go one direction at a time so i just added the vectors to go more than one direction

Oh thanks I didn’t even notice that haha

1 Like

This works, thank you! Just one more thing, why does this work, but changing the CFrame doesn’t? (If I had to guess, is it because the constraint is constant, and changing it every frame causes it to fight?)

I did more testing and I think I figured it out. It seems like when applying Linear Velocity to the character and instantly changing it’s position, it seems to calculate that distance along with the time taken therefore applying force to the player, and because the transition happens instantly, a lot of force is being applied to the player which then causes the jitter as you’ve seen.

This could also be due to the fact the player is constantly realigning itself to the standing position which contributes to the jitterness.

However when using Align Orientation, it will smoothly transition the character instead of changing the rotation instantly. Mostly likely similar to using TweenService/Lerp

There is more details here in the comment I made above in case you missed it.

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