How to rotate CFrame on two global axis's with other CFrame position properties set?

I am adding a new parameter to a bowling game I am helping develop where you can now add forward/reverse roll, instead of before where it was just the left/right hook. The way how it works is that when you pick up the bowling ball, it creates a CFrame to have the bowling ball in front of your camera locally. When you drag your mouse left or right it will modify the CFrame to have the ball spin in that direction. When the ball is thrown it creates a BodyAngularVelocity with the parameters set to roll down the lane.
I got the BodyAngularVelocity to work correctly with the new forward/reverse roll parameter but I am running into an issue with the CFrame where when you apply both roll and hook(X and Z axis) the bowling ball will spin wonky and in random directions.
Here is a video showing what I want the visual to look like(This is using a BodyAngularVelocity):


This is what is happening using the CFrame with the current way it is scripted. Notice how it keeps switching between rolling forwards and backwards:

Here is the code for the CFrame:

Target.CFrame = self.Camera.CFrame --Target is the ball and Camera is the location of the camera
			* CFrame.new(self.HoldOffset, -1.2 + self.CurrentY, 0) --Offsets the ball so it is in front of the camera
			* CFrame.Angles(0, 0, self.Ticks) --self.Ticks is how much the ball hooks left or right
			* CFrame.Angles(self.TiltTicks, 0, 0) --self.TiltTicks is how much the ball rolls forwards or backwards
			+ self.Camera.CFrame.LookVector

I have tried many solutions with no success. One solution I tried was putting the CFrame.Angles before the Camera.CFrame but then the bowling ball will move off-screen. Another solution I tried was doing:

Target.CFrame = ((CFrame.Angles(0, 0, self.Ticks) * CFrame.Angles(self.TiltTicks, 0, 0)) * (self.Camera.CFrame-self.Camera.CFrame.Position)) + self.Camera.CFrame.Position
			* CFrame.new(self.HoldOffset, -1.2 + self.CurrentY, 0)
			+ self.Camera.CFrame.LookVector

This lead to an error “attempt to multiply a Vector3 with an incompatible value type or nil” and I am unsure how I can modify it to accept the offset and LookVector.
I also tried using :ToWorldSpace which also did not work(Bowling ball was never on screen) unless I am utilizing it incorrectly.
Any help is appreciated.

Is there a reason you must use CFrame?

If you’re going to use physics after the ball is thrown, it might make sense to use the same physics to spin it (with your same BodyAngularVelocity), and use a AlignPosition | Documentation - Roblox Creator Hub constraint .

Set Attachment0 as the ball and Attachment1 as the player HumanoidRootPart) to keep it in front of the player while allowing it to rotate freely.

Just a suggestion – this would have the added benefit of the spin of the ball once its thrown exactly matching the spin while you’re holding it, with no extra work to make the CFrame match speed.

You’ll want to rotate it against the world axis. Currently, composing CFrames together will make it on relative axes.

Target.CFrame = ...
	* CFrame.fromAxisAngle( Target.CFrame:VectorToWorldSpace(Vector3.new(0,0,1)), self.Ticks)
	* CFrame.fromAxisAngle( Target.CFrame:VectorToWorldSpace(Vector3.new(1,0,0)), self.TiltTicks)
...

Unfortunately this seemed to make it worse:


I did

Target.CFrame = self.Camera.CFrame
			* CFrame.new(self.HoldOffset, -1.2 + self.CurrentY, 0)
			--* CFrame.Angles(0, 0, self.Ticks)
			--* CFrame.Angles(self.TiltTicks, 0, 0)
			* CFrame.fromAxisAngle( Target.CFrame:VectorToWorldSpace(Vector3.new(0,0,1)), self.Ticks)
			* CFrame.fromAxisAngle( Target.CFrame:VectorToWorldSpace(Vector3.new(1,0,0)), self.TiltTicks)
			+ self.Camera.CFrame.LookVector

The reason why I would really prefer to use CFrame is that the main code for throwing the bowling ball is very complicated in many different ways.

I don’t think you can do this with just Ticks and TiltTicks.

But if you have the angular roll speeds (I’m assuming in rads/second) you could calculate the roll axis like

local rollAxis = Vector3.new(-rollFrontBackSpeed, 0, rollRightLeftSpeed)

That’ll give you a vector pointing to the left of the roll direction, which you could use for fromAxisAngle:

local rollAmount = -- update this like you update `Ticks`
                   -- but at a speed of rollAxis.Magnitude

Target.CFrame = self.Camera.CFrame
	* CFrame.new(self.HoldOffset, -1.2 + self.CurrentY, -1)
	* CFrame.fromAxisAngle(rollAxis, rollAmount)

Edit: RE: @MrNicNac’s point, I don’t think you need to transform your axes to world space, since you’re trying to make things relative to the Camera, and you’re already multiplying by the camera’s CFrame.