Strange CFrame Rotation Behavior when using CFrame.Angles()

When I set the CFrame rotation of a Part using multiple axes, the part rotates in a strange way.

This Part, called TestBlock, is rotated to an x-angle of -15 degrees (which is converted to radians for CFrame.Angles().). I expect that, if I also rotate the TestBlock on the Y-axis, the part will still be rotated on the X-axis. I believe this because I can set the desired rotation in Studio, as shown in this image. I can set the rotation to (-15, 90, 0) (XYZ order) directly in Studio.

But when I use CFrame.Angles() to rotate the TestBlock in the Y-axis as well, the part also rotates on the X-axis. This is demonstrated in the Properties section of the TestBlock, as the X-value does not remain at the desired X-axis value of -15 degrees.

To rotate the TestBlock as shown in the above video, I’m using the following code.

--- Rotate the block arond the Y-axis
local block = workspace.TestBlock
local counter = 0

while task.wait(1) do
	block.CFrame = CFrame.Angles(math.rad(-15),math.rad(counter),0)
	
	counter += 45
end

I’m expecting that if CFrame.Angles() is given an X-value of -15, a Y-value of counter, and a Z-value of 0, the Part’s X-value will be -15, the Y-value will be equal to counter, and the Z-value will be equal to 0. But the video shows that doesn’t happen, which is why I’m confused.

I’m not setting the rotation directly using a Vector3 value, as I need to use CFrames to rotate models and cameras. I’m using a Part to show the unexpected rotational behavior.

What am I missing? Thank you for any help!

CFrame.Angles is equivalent to CFrame.fromEulerAnglesXYZ which applies rotation on axis in the order X, Y, Z. You’re probably looking for CFrame.fromEulerAnglesYXZ which will rotate about the Y axis first, then X, then Z.

3 Likes

Usually if you do It separately it works as Intended.

--- Rotate the block arond the Y-axis
local block = workspace.TestBlock
local counter = 0

local offsetCF = CFrame.Angles(math.rad(-15),0,0)

while task.wait(1) do
	block.CFrame = offsetCF * CFrame.Angles(0,math.rad(counter),0)
	
	counter += 45
end

Wait, there’s no problem this is intended, I’m pretty sure your confused because the orientation is most likely converted to WorldSpace/Global or in other words its not aligned to its own axis.

Im rotating the part LOCALLY (Blue L) on a slanted angle


Note that 164.999 is equivalent to 165 which is 15 degrees lower than 180, and the rotation on Y is as intended and also look that Z flips it ~ -180 to ‘compensate’.

local rx, ry, rz = block.CFrame:ToEulerAngles()
print(math.deg(rx), math.deg(ry), math.deg(rz))

This is equivalent to just using a single CFrame.Angles with a non-zero X and Y argument. In order to fix the issue you’d have to do:

local offsetCF = CFrame.Angles(math.rad(-15), 0, 0)
while task.wait(1) do
	block.CFrame = CFrame.Angles(0, math.rad(counter), 0) * offsetCF
	counter += 45
end

However, this equivalent to using CFrame.fromEulerAnglesYXZ as I mentioned.

Yeah but there was nothing wrong, nor is anyone’s code here wrong, he misinterpreted it.

Yours LOOKs like its the solution because it rotates it first on Y then is tilted, however In my case I tilt it first and rotate it which would increase computation power causing this odd effect, so I’d guess that order matters most with CFrames

did you put ANYTHING but a 0 in the first parameter (x) of cframe.angles(x,y,z)

if not it should look like this: cframe.angles(0,y rotation amount,0)

Thank you all so much for your help, I appreciate everyone’s contributions to this topic!

If I understand right, does this mean that changing rotation in Studio’s Properties (works as I intended) uses the local axes, while CFrame.Angles() (doesn’t work as I intended) uses global axes?
After further testing using the Rotate tool, I notice that using the Rotate tool does sometimes change the CFrame’s numerical values in Properties in unexpected ways, but I see what you mean that this is intended.

Overall, is this problem Global vs. Local rotation?

When I rotate the part manually using the Rotate tool, rotating XY or YX orders both seem to produce the same result. I think the rotation is local because the axes seem to rotate along with the part, as opposed to global rotation where the axes’ rotation would be unaffected. Is that correct, even though no L is present on my rotation/selection?

1 Like

I think its because your still using the Roblox’s Old Transformation System, I have the improved one enabled in the Beta Features; So it would show up the L when I toggle it with Ctrl + L

Other than that, CFrames are confusing, but they ‘work’.

1 Like

I can rotate the part along the y-axis if I do:

--- Rotate the block arond the Y-axis
local block = workspace.TestBlock
local counter = 0

while task.wait(1) do
	block.CFrame = CFrame.Angles(0,math.rad(counter),0)
	
	counter += 45
end

This will rotate the part around the y-axis, but the problem is when I want to rotate a Part around two axes. I expect that the parameters of CFrame.Angles() will become the rotation values in the Part’s Properties tab, but instead the part rotates differently than expected.

so you want to rotate Y for a bit then rotate X?
or u could js do this:

local block = workspace.TestBlock
local counter = 0

while task.wait(1) do
	block.CFrame = CFrame.Angles(0,math.rad(counter),0)
	block.CFrame = CFrame.Angles([ROTATE_VAL_HERE],0,0)
	counter += 45
end

My goal is to set specific rotation values, as shown in the Properties tab. I want to set the x-axis’ rotation to -15 and the y-axis’ rotation to 90. But when I use those values in CFrame.Angles(), I don’t get an x-rotation of -15 and a y-rotation of 90.

This image is the result of the following code:

--- Rotate the part slightly downward
local block = workspace.TestBlock

block.CFrame = CFrame.Angles(math.rad(-15),math.rad(90),0)

I would expect each specified value to be inserted into the rotational value as shown in the Properties tab, but instead I get a different Part rotation and different values in Properties. The above code does not give me an expected rotation of (-15, 90, 0), but instead gives me a strange and unexpected rotation, (0, 90, -15).

Ctrl + L works for me as well! So this is the toggle that switches between global and local rotation for the Rotate tool! Good to know, thank you! :slightly_smiling_face: :+1:

then use math…

block.Rotation = Vector3.new(block.Rotation.X + -15, block.Rotation.Y + 90, 0)

pretty sure it works

When I tried this code with the Part initially set with a rotation of (0,0,0), I still obtained a rotation of (0, 90, -15). Strangely, the original problem persists regardless of whether CFrame or Rotation is used.

strange, the logic should be,

logic {
1. rotate to rotation Y + 90 and rotation X + -15
2. set rotation variable to new vector3
3. loop again
}

by that logic my code should have worked, im so confused

edit: bro u forgot the loop :skull:

1 Like

heres the code that worked for me:

local block = script.Parent
while task.wait(1) do
	block.Rotation = Vector3.new(block.Rotation.X + -15, block.Rotation.Y + 90, 0)
end

The only reason I originally included a loop was just to clearly show how the Part rotates as an x-rotated part was rotated on the y-axis. If the part rotates once to (-15, 90, 0), that’s all I need. :slightly_smiling_face::+1:

local block = script.Parent
while task.wait(1) do
block.Rotation = Vector3.new(block.Rotation.X + -15, block.Rotation.Y + 90, 0)
end

use this code it worked idk why it didnt for u when u tried

(please set this post as solution when it works)

You can use Hierarchy-Saver to convert instance property to code like cframe