How can I set a CFrame's angle on one global axis?

I have code to tilt the player’s character with the terrain beneath them so that they align with terrain’s slopes. Now I want to give the player the ability to rotate their character left/right using their controls. However, multiplying a CFrame by CFrame.Angles rotates them relative to their previous orientation, and I don’t want that. I want to control the exact rotation they’re aiming for, not rotate them incrementally. I know how to get the CFrame to represent the player’s left-right controls, but I can’t figure out how to combine this CFrame with the CFrame that aligns them with the terrain in order to allow them to turn their character left or right while aligning with terrain

This is (I think) what I need to achieve that:

local TerrainAlignment = CFrame.fromMatrix(Shared.PrimaryPart.CFrame.Position, alignRightVector, alignLookVector:Cross(alignRightVector), -alignLookVector)
TerrainTiltGyro.CFrame = TerrainAlignment
local CameraLookAt =  CFrame.lookAt(Shared.PrimaryPart.Position, Shared.PrimaryPart.Position + game.Workspace.CurrentCamera.CFrame.LookVector)
-- I want to combine the TerrainAlignment CFrame with the global Y orientation of
-- the LookAt CFrame so that the character can rotate left-right to align with the
-- camera on the global Y axis while aligning with the terrain on all other axes

I think this code shows what I’m trying to do, but it doesn’t work; there’s so many different functions for CFrame:toWhateverAngles and CFrame.fromWhateverAngles, I’m not sure what translates to what

local TerrainAlignment = CFrame.fromMatrix(Shared.PrimaryPart.CFrame.Position, alignRightVector, alignLookVector:Cross(alignRightVector), -alignLookVector)
local x, y, z = TerrainAlignment:ToOrientation()
local x2, y2, z2 = CFrame.lookAt(Shared.PrimaryPart.Position, Shared.PrimaryPart.Position + game.Workspace.CurrentCamera.CFrame.LookVector):ToOrientation()
TerrainAlignment = CFrame.new() * CFrame.Angles(x, y2, z)

I think another solution I could do is get the difference between the TerrainAlignment’s orientation on the Y global axis with the orientation on the Y global axis of the LookVector’s CFrame and then increment the TerrainAlignment’s CFrame by the difference, but I’m not sure how to get those values

Something else I tried to do is use different instances to control orientation on different axes, but try as I might I couldn’t figure it out. BodyGyro has the ability to limit torque on specific axes which helps with that, but I’d like to not have to rely on BodyGyro given that it’s deprecated

Thank you very much for any feedback

Hey there,
So what I did to make the player align to slopes was this:

local moveDirection = humanoid.MoveDirection

local surfaceRight = moveDirection:Cross(normal) 
--[[^^^ We're using moveDirection instead of LookVector because
we want the player to rotate in the direction they're moving, instead of being in a fixed look direction]]

local surfaceAlignment = CFrame.fromMatrix(position, surfaceRight, normal)
alignGyro.CFrame = surfaceAlignment -- Or you can use AlignOrientation, whichever one you prefer

Now what sucks is that you have to make your custom movement for WASD/Joystick and jumping since the player will slightly sink into the ground, but easiest way is to just use a BodyVelocity or any body mover and change it’s velocity/direction to the humanoid’s MoveDirection multiplied by its WalkSpeed (And it’ll work for all platforms which is neat)

If you need anymore help just lemme know :slightly_smiling_face:

Oh yeah and you’ll need some raycasting to find the normal vector of the ground of where the player is standing at if you’re not already doing that