I'm trying to mimic a third person camera in an old game I used to play, and I'm using math.atan2() to calculate the angle at which the camera should rotate, relative to the head (so it's looking at the head). However, when I move my character via the mouse (mouse is locked to the center of the screen), the rotation goes crazy.
I want to 'clamp' it, or prevent it from going completely bonkers.
Here's the code for the camera:
--> Constantly update the camera. RS:BindToRenderStep( "Camera", 201, function() local z = camera.CFrame.Z - character.Head.CFrame.Z local y = camera.CFrame.Y - character.Head.CFrame.Y pitch = atan2( y, z ) root.CFrame = cf( root.Position ) * ca( -0, rad( yaw * 5 ), 0 ) camera.CFrame = camera.CFrame:lerp( root.CFrame * cf( 0, -2.5, 7.5 ) * ca( pitch, 0, 0 ), 0.4 ) end )
root is the character's HumanoidRootPart.
cf and ca are both CFrame.new() and CFrame.Angles() respectively.
atan2 is of course math.atan2().
I apologize if what I'm asking is too vague. Here's a gif to illustrate my problem:
I want the camera to pitch up and down, always pointing at the head as the mouse moves up and down (not yet implemented, but I manipulated the CFrame so you could see the angle) without doing what it does now when I rotate the character.
Any help would be appreciated, thank you!
Ah, periculosa, you're so close! Your solution would be perfect for a 2-dimensional world. You want to find the vertical angle between the camera and the head, which means you need to calculate the angle between a line and a plane, not between 2 lines.
Notice in your GIF that the strange rotation happens only when you're facing in a certain direction - this is a hint that you've overlooked one of the 3 spatial dimensions.
Take these two lines:
local z = camera.CFrame.Z - character.Head.CFrame.Z local y = camera.CFrame.Y - character.Head.CFrame.Y
What will happen when the character is facing directly in the x-direction, but not in the z-direction? z
will become very tiny, so the vertical difference would be relatively much larger than this negligible horizontal difference. atan2
will interpret this as looking up at a very steep angle. This is confirmed empirically in your tests; when facing a certain direction, the camera flips upwards.
What is the solution? Instead of finding the difference in just the z-axis, you want to find the whole horizontal distance, that is, the length of a 2-dimensional vector in the horizontal plane x-z.
--Remove vertical component local camera_horizontal = Vector3.new(camera.CFrame.X, 0, camera.CFrame.Z) local head_horizontal = Vector3.new(character.Head.CFrame.X, 0, character.Head.CFrame.Z) --Find magnitude of difference local diff = (camera_horizontal - head_horizontal).Magnitude pitch = atan2( y, diff )
EDIT: The angle didn't need to be clamped, per se, but I think the calculation of the angle was incorrect and I addressed that. When you do need to clamp values into a range, which you probably will want to do for some effects when creating your own Scriptable
cameras, refer to evaera's suggested technique.
Really having trouble understanding what you mean still, but you can use this code to clamp an angle.
angle = math.max(-2.8, math.min(angle, 0.4))
where -2.8 is the bottom, and 0.4 is the top. Note that this is in radians