Trouble getting CFrame rotation components

I have a part that keeps rotating on its local X and Z axis, but I want it to have always the same rotation on the local Y axis, this is the code I have so far:

local X, Y, Z = Part.CFrame:ToEulerAnglesXYZ()
local Difference = RandomAngle - math.deg(Y)
Part.CFrame = CFrame.lookAt(TargetPos, TargetPos + SurfaceNormal) * CFrame.Angles(math.rad(90), math.rad(Difference), 0)

So what I’m trying to do here is get the Y Rotation of the Part’s CFrame and then subtract it from what I want the rotation to be, and then multiply its rotation on the Y by the result. So let’s say I want it to always be rotated 100 degrees and its rotated by 30 degrees, 100 minus 30 would give me 70 and then by rotating it 70 degrees it would get it to 100 degrees.

1 Like

You should generally avoid using XYZ for rotation. I might just be daft, but I’m not really understanding the code or the example at the bottom. Could you take a step back and reword it in a more generalized way? What is this aiming to accomplish?

I’m trying to keep the local Y rotation of a part the same.

https://cdn.discordapp.com/attachments/851587139610214460/856607257625231374/fxREpMqrPh.mp4

you see how it rotates on the Y? I don’t want that to happen

1 Like

I see. I’ve never used terrain water like this, is the SurfaceNormal part the thing that causes it to roll with the waves? And then you are rotating it 90 degrees X to make it level with the water? And then you are trying to cancel Y rotation?

It’s not terrain water, it’s mesh deformed water. And yes, when I set the cframe to look at the water’s normal, it’s what causes it to rotate, which causes it to rotate on the Y axis too which I don’t want.

Give me a few minutes and I’ll let you know.

Put these constant variables at the top of your script. I used an all capitals naming convention since they are constants in this code.

local RIGHT_VECTOR = Vector3.new(1, 0, 0)
local UP_VECTOR = Vector3.new(0, 1, 0)
local BACK_VECTOR = Vector3.new(0, 0, 1)

For this code I tried to work in all of the variables you were using, including RandomAngle. You might need to fiddle with it a little though.

Part.CFrame = CFrame.fromMatrix(TargetPos, SurfaceNormal:Cross(BACK_VECTOR).Unit, SurfaceNormal, SurfaceNormal:Cross(RIGHT_VECTOR).Unit) * CFrame.fromAxisAngle(UP_VECTOR, math.rad(RandomAngle))

Let me know if something is amiss and I will see what I can do after I get back from work. Good luck!

Edit for those curious. Here is how this works.

A CFrame matrix is a really annoying 4x4 matrix of numbers, but only 12 of the 16 numbers in the matrix are important. The numbers come in four groupings of three, and they can be represented by Vector3s. The first is position. Every CFrame of course has a Vector3 to describe position. The second V3 describes where the right side of the part is pointing. If this number is 1,0,0, then so far the part is in its original orientation since the X (right) side of the part is pointing in the positive X direction. The third V3 describes the Y (top) side of the part. If this number is 0,1,0 then the part is so far unrotated. If the number is 0,-1,0 then we can ssume that the top side of the part is facing downwards. The fourth V3 describes the Z (back, not front) direction.

We know the position, that’s TargetPos in your code. We also know which direction the top side of the part needs to face. That information is your variable SurfaceNormal which is conveniently a unit vector (magnitude of 1) which describes a direction. So far so good, we have a position and a significant direction. Your code did that well enough. But we need another direction to keep it from rotating around. I used the static vectors for right and back, 1,0,0 and 0,0,1. The problem here is that all three axes need to be perpendicular to each other, and if we used a raw 1,0,0 then we would have problems whenever the water wasn’t perfectly level.

The solution is the cross product. The cross product creates a perpendicular vector from two other vectors. If we did use the ideal 1,0,0 and 0,1,0, then the cross product would be the missing third vector, 0,0,1. In our case however, it took one of these constant vectors and the upwards vector to create our third vector. If our right vector was 1,0,0 and the waves beneath us were rotated 45 degrees to be 0, .707, .707, then the cross product would tell us that the Z vector should be 0, .707, -.707. The numbers themselves don’t need to make a tremendous amount of sense, but it just allows us to add some extra information to keep the part from rotating, and the cross product allows us to keep the directions perpendicular to each other.

After this, I used fromAxisAngle which uses a form of advanced Thaumaturgy to rotate the part around the axis specified. You can just use CFrame.Angles instead, but I avoid XYZ rotations like the plague it is.

1 Like

oh wow, that worked, thank you! (Didn’t understand much tho ngl)