Currently I’ve been trying to limit the body gyro rotation (specifically the pitch) of my model through CFrame:ToEulerAnglesXYZ() like in the local script shown below:

while wait(0.1) do
local x,y,z = SubRootPart.RotationGyro.CFrame:ToEulerAnglesXYZ()
print(math.round(math.deg(x)))
if WHeld == true and math.round(math.deg(x)) < 30 then
SubRootPart.RotationGyro.CFrame = SubRootPart.CFrame * CFrame.Angles(math.rad(5), 0, 0)
end
end

Perhaps the order the angles are being applied to are causing the problem.

Here is an example project where I used CFrame.lookAt to make a turret look at a desired position then limited the angles using CFrame:To orientation() with math.clamp to limit the angle of elevation and depression. The problem should be similar to this one.

Edit: also how does your subrootpart look like, is the front face of this part oriented with the front face of the submarine model correctly?

I suggest making sure the root part is facing the correct direction in order to avoid having an offset rotation that needs to be CFramed in order to fix.

I actually saw your post on limiting CFrame rotation. However I don’t quite understand how I would use math.clamp in this situation.
And yes the orientation of the root part is at 0, 0, 0 and the model does move forward correctly. So I’d assume it is oriented correctly.

Hmm, if I were to apply what I did previously, I would do this:

while wait(0.1) do
if WHeld == true then
SubRootPart.RotationGyro.CFrame = SubRootPart.CFrame * CFrame.Angles(math.rad(5), 0, 0)
end
local x,y,z = SubRootPart.RotationGyro.CFrame:ToOrientation()
local clampedX = math.clamp(math.deg(x),-30,30)--elevation and depression angles
clampedX = math.rad(clampedX)
SubRootPart.RotationGyro.CFrame = CFrame.fromOrientation(clampedX,y,0)
end

This piece of code clamps the elevation and depression angle of the model assuming the model has no Z-axis roll because if it did have Z axis roll things would get a little more confusing and the X angle won’t represent the angle of elevation/depression from the horizontal surface but I’ll suggest you see that for yourself if the code piece does work.