How to correctly prevent green axis rotation with CFrames?

I tried to upload the videos on the Devforum but I received error messages so instead, I posted them on YouTube

The blue part’s orientation must match the red part’s orientation on the red and blue axis only. The blue part’s green axis must be left untouched.
To make sure my question is clear, here’s a video that shows the desired behavior of the blue part: https://www.youtube.com/watch?v=XRUQH9Lw8rY
Now let’s see what I get.
Everything works perfectly when I’m trying to rotate the part on all 3 axis, as you can see here: https://www.youtube.com/watch?v=bD_p5ES_3xs

But when I try to ignore the green axis, the blue part becomes corrupted when it tries to rotate after the red part’s red or blue axis!
You can view this bug here: https://www.youtube.com/watch?v=r9Hg6K8sjfs

How do I fix that?

This is my current code:
(Part0 is the red part and Part1 is the blue part)

game:GetService("RunService").RenderStepped:Connect(function()
     local rv = workspace.Part0.CFrame.RightVector
     local uv = workspace.Part0.CFrame.UpVector
     local bv = -workspace.Part0.CFrame.LookVector

     local rv1 = workspace.Part1.CFrame.RightVector
     local uv1 = workspace.Part1.CFrame.UpVector
     local bv1 = -workspace.Part1.CFrame.LookVector

     workspace.Part1.CFrame = CFrame.new(6, 9, -25, rv1.X, uv1.X, bv1.X, rv.Y, uv.Y, bv.Y, rv1.Z, uv1.Z, bv1.Z) 
end)
1 Like

Explaining why this happens is hard, but pretty much, the rotational vectors of a CFrame have to be orthogonal to each other, else a CFrame is messed up, and will result into weird behavior, like you experienced. To put it simply, never never play with the rotation vectors when using the CFrame.new constructor, it doesn’t work the way you think it does.

What yu can do is rather simple. Each time you change the Part1 CFrame, to a new CFrame that has the same position (because you wanna keep the same position, doing something like CFrame.new(6, 9, -25) results into hardcoded code, meaning if you change the position of Part1, this will not work and revert it back to 6, 9, -25, to make it work wherever Part1 is, just do CFrame.new(Part1.CFrame.Position), generally manually inserting coordinates is really bad), and multiply it by a CFrame.Angles that has the same rotation Part1 had before for the Z axis (green axis), and for the X and Y rotations put the Red part’s ones.

tl;dr, but I hate spoonfeeding

local x, y, _ = Part0.CFrame:ToEulerAnglesYXZ() --this function returns the three rotations for the three axes, pretty much the numbers inside of CFrame.Angles, we only care about the first and second, x and y
local _, _, z = Part1.CFrame:ToEulerAnglesYXZ() --this is the z rotation of Part1 which is the one we want to keep
CFrame.new(Part1.CFrame.Position) * CFrame.Angles(x, y, z)

There might be a better way to do this.

After messing around with your script, I was able to achieve what I wanted. Thanks!

1 Like