CFrames: Changing the X and Y angle introduces unwanted Z

I have been stuck on this problem for a while. What is basically hapenning is, that i have a X, Y, Z, in a vector, and i apply them into a CFrame:

local Offset =  CFrame.Angles(
	math.rad(RotationOffset.X),
	math.rad(RotationOffset.Y),
	math.rad(RotationOffset.Z)
)

I have a specific effect, that doesn’t use Z, and only X and Y, but Z is changed for some reason.
After printing this by using CFrame:ToEulerAnglesXYZ(), Z will always be 0.
Offset is equal to: (note that this is a increase of that rotation each frame)
0.001771788578480482 0.0054963212460279465 -0 at the start
0.000044158601667732 -0.00014911283506080508 -0 and this is it at the end.
everywhere in between Z is equal to 0 (or -0).

Afterwards i do this calculation: (happens each frame, DesiredCFrame only changes position in my test cases, and never changes rotation.)

local EndCFrame = DesiredCFrame * EffectsOffset 
... -- EndCFrame is not affected by any way in between these lines, only used for some other calculation
Camera.CFrame = EndCFrame

But if i print the Eulerangels of the EndCFrame then:
start of the effect: -0.062286216765642166 -0.15526291728019714 -0.009644108824431896
end of the effect: -0.06207619607448578 -0.15618515014648438 -0.10972192138433456

I have not moved my camera, and everything i did, was simply add that rotational offset to a position.
This extra Z causes some unwanted screen rotation. Note that i still want you to be able to rotate in the Z direction, but when i only move my x and y, z shouldn’t move.

So when i change the X and Y, the Z also changes for no reason. This happens when the cameratype is set to scriptable.

I can also see that they slightly affect eachother. But how do i make it, so that this unwanted rotation doesn’t happen anymore.

1 Like

You don’t understand how the Euler math works.
There is no “XYZ” in the typical sense as you think.
It’s a pitch, yaw, and roll.

Please just use CFrame.fromEulerAnglesYXZ() instead if you can’t understand Euler’s math.
Alternatively, you can use my plugin to precompile the constructor:

uh, according tothe wiki, CFrame.Angles is equal to fromEulerAnglesXYZ. So i don’t see the point in changing that, as it doesn’t change anything.

And i don’t understand what your pluging has to do with my problem?

heres the fix:

it just reconstructs the angle from nothing

local x, y, z = EndCFrame:ToOrientation()
EndCFrame = CFrame.Angles(x, y, 0)

Just why…
just do cf.Rotation to get a clean cframe with no translation.

because it still gives him unwanted Z?
recoupling the cframe and ignoring Z is better

local Offset =  CFrame.Angles(
	math.rad(RotationOffset.X),
	math.rad(RotationOffset.Y),
	0
)

EndCFrame = DesiredCFrame * Offset

Pretty much this should work too

1 Like

so what the problem here is that because i am applying this every frame this code will just make me not able to rotate my camera correctly:

local x, y, z = EndCFrame:ToOrientation()
EndCFrame = CFrame.Angles(x, y, 0)

the problem with this code, is that i sitll want to be able to Change the Z angle trough its effect, but later on. And what is happening, is that even with this, the effect still happens, as applying the X and Y, causes the Z to change (even if the offset of Z is 0). This is what i am trying to fix.

local Offset =  CFrame.Angles(
	math.rad(RotationOffset.X),
	math.rad(RotationOffset.Y),
	0
)

EndCFrame = DesiredCFrame * Offset

Thanks for responding, got any other ideas?

then apply yaw first and then pitch

local yaw   = math.rad(RotationOffset.Y)
local pitch = math.rad(RotationOffset.X)
local roll  = math.rad(RotationOffset.Z)

--// yaw → pitch → roll
local Offset =
    CFrame.Angles(0, yaw, 0) --// world-space yaw
    * CFrame.Angles(pitch, 0, 0) --// local pitch
    * CFrame.Angles(0, 0, roll) --// optional roll if you want it

Unfortunately, this also doesn’t work. I have tried to add them seperately, but i have no clue why this keeps happening.

I feel like it only should work by changing the line:

local EndCFrame = DesiredCFrame * Offset

Because as i have mentioned before in my original post, the offset in itself has no rotation in the z after creating it, but the EndCFrame suddenly has. And thats what i don’t understand why it happens.

can you send a video of what’s happening and full code?

Well, i’m working on a module which is 700 lines long and i got commisioned for, so let me first turn it into a smaller place file where only that problem is, and i will share it here

just send the loop or wtv that handles the cframe

Alright. I made the code as small as possible so that its easier to get the problem, and i have left out code, that does NOT affect the problem in any way.

RS:BindToRenderStep("CameraService_Offset_Handler", Enum.RenderPriority.Camera.Value, function(DeltaTime)
	local PositionOffset = Vector3.zero
	local RotationOffset = Vector3.zero
	
	for index, Value in ipairs(OffsetEffects) do
		local Offset = Value:Update(DeltaTime)
		PositionOffset += Offset.PositionOffset or Vector3.zero
		RotationOffset += Offset.RotationOffset or Vector3.zero
	end

	local EffectsOffset = CFrame.new(PositionOffset) * CFrame.Angles(
		math.rad(RotationOffset.X),
		math.rad(RotationOffset.Y),
		math.rad(RotationOffset.Z)
	)

	local DesiredCFrame = Camera.CFrame
	
	if CurrentCutscene then DesiredCFrame = CurrentCutscene(DeltaTime) end
	
	local EndCFrame = DesiredCFrame * EffectsOffset
	Camera.CFrame = EndCFrame
end)

RS:BindToRenderStep("CameraService_Offset_Handler", Enum.RenderPriority.Camera.Value, function(DeltaTime)
	local PositionOffset = Vector3.zero
	local RotationOffset = Vector3.zero

	for _, Value in ipairs(OffsetEffects) do
		local Offset = Value:Update(DeltaTime)
		PositionOffset += Offset.PositionOffset or Vector3.zero
		RotationOffset += Offset.RotationOffset or Vector3.zero
	end

	local yaw   = math.rad(RotationOffset.Y)
	local pitch = math.rad(RotationOffset.X)
	local roll  = math.rad(RotationOffset.Z)

	local EffectsOffset =
		CFrame.new(PositionOffset)
		* CFrame.Angles(0, yaw, 0)
		* CFrame.Angles(pitch, 0, 0)
		* CFrame.Angles(0, 0, roll)

	local DesiredCFrame = CurrentCutscene and CurrentCutscene(DeltaTime) or Camera.CFrame
	local EndCFrame = DesiredCFrame * EffectsOffset
	Camera.CFrame = EndCFrame
end)

try this real quick

Thats exacly what i tried out after you told me to do so:

and unfortunately, it didn’t work

i need a clip of whats happening

1 Like
External Media

I am printing: print(EffectsOffset:ToEulerAnglesXYZ()), which is what you see.

Now suddenly, i do see the z going up, which i havent been able to with my previous code? :thinking:

What I see is that you want to make a local offset. The simplest way to do this is by component.

local x,y,z = DesiredCFrame:ToEulerAnglesXYZ()
local EndCFrame =  CFrame.Angles(x+RotationOffset.X, y+RotationOffset.Y,z+RotationOffset.Z) + DesiredCFrame.Position

Since my code runs it every frame, this will cause every frame for itself to multiply, and it will turn .01 into 0.02, then into 0.04 which will cause the camera to like fly around.

wait this actually doesn’t make sence why that is happening, but the camera is flying all around.

Edit: Oh, you have to convert it into radials

Edit 2: I think it might work, let me test