Hey, I’ve been having a hard time trying to figure this one out.
I have a security camera enemy that rotates to its left and right while it is in its “Idle State” (searching for any players that happen by its trigger volume).
But when it detects a player and enters into its “Observe State” (and rotates to look at them) I’d like to have the camera’s yaw rotation clamped between 90 and -90 degrees, and its pitch rotation clamped between 20 and -20 degrees (to prevent the camera from “pulling an Exorcist” and rotating beyond what is believable given its mesh’s shape and form).
However, I can’t seem to figure out just how to go about clamping it’s rotation in a way that works.
Here’s the two methods I’ve got so far for both Idle State and Observe State. Sorry though if the code looks sloppy here, this is the first time I’ve used these forums to put any code up.
Camera stuff is pretty tricky… it might be best to use Cinemachine from the Unity Package Manager. Pretty sure it has all kinds of clampy boundsy stuff available.
I usually like to take a linear algebra approach when dealing with rotations. Clamping rotation can indeed be tricky. Here is a quick thing I whipped up that should do what you want.
using UnityEngine;
public class ConstrainedLookAtExample : MonoBehaviour
{
[SerializeField] private float m_YawLimit = 90f;
[SerializeField] private float m_PitchLimit = 20f;
// The head that will actually swivel
[SerializeField] private Transform m_CameraHead;
// Object head is tracking
[SerializeField] private Transform m_TrackedObject;
private void Update()
{
// Can we look at target?
if (CanLookAt(m_TrackedObject.position, m_CameraHead.position))
// Look at target
m_CameraHead.LookAt(m_TrackedObject.position, Vector3.up);
}
private bool CanLookAt(Vector3 targetPosition, Vector3 headPosition)
{
// Get direction from head to target
var direction = (targetPosition - headPosition).normalized;
// Get yaw vector (Remove pitch)
var yawDirection = Vector3.ProjectOnPlane(direction, transform.up).normalized;
// Set limits relative to results of dot products.
var yawLimit = Mathf.Sin((90 - m_YawLimit) * Mathf.Deg2Rad);
var pitchLimit = Mathf.Sin((90 - m_PitchLimit) * Mathf.Deg2Rad);
// Check if we are within limits.
return Vector3.Dot(transform.forward, yawDirection) > yawLimit
&& Vector3.Dot(direction, yawDirection) > pitchLimit;
}
}
Math can be simplified by using the Vector3.Angle method.
private bool CanLookAt(Vector3 targetPosition, Vector3 headPosition)
{
// Get direction from head to target
var direction = (targetPosition - headPosition).normalized;
// Get yaw vector (Remove pitch)
var yawDirection = Vector3.ProjectOnPlane(direction, transform.up).normalized;
return Vector3.Angle(yawDirection, transform.forward) < m_YawLimit
&& Vector3.Angle(direction, yawDirection) < m_PitchLimit;
}