Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Clamp Rotation of Security Camera Actor

Discussion in 'Scripting' started by TheStarWarrior, Aug 3, 2021.

  1. TheStarWarrior

    TheStarWarrior

    Joined:
    Jan 6, 2020
    Posts:
    2
    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.

    Code (CSharp):
    1.     public void Idle()
    2.     {
    3.         if (_actorRotYaw >= actorStatic.actorYawClamp)
    4.         {
    5. Vector3 tempRightDir = new Vector3(_camObj.transform.position.x - 90f, _camObj.transform.position.y, _camObj.transform.position.z);
    6.  
    7. _targetRotation = Quaternion.LookRotation(tempRightDir - _camObj.transform.position);
    8.         }
    9.  
    10.         else if (_actorRotYaw <= -actorStatic.actorYawClamp)
    11.         {
    12. Vector3 tempRightDir = new Vector3(_camObj.transform.position.x + 90f, _camObj.transform.position.y, _camObj.transform.position.z);
    13.  
    14. _targetRotation = Quaternion.LookRotation(tempRightDir - _camObj.transform.position);
    15.         }
    16.  
    17.  _camObj.transform.rotation = Quaternion.RotateTowards(_camObj.transform.rotation, _targetRotation, actorStatic.actorDefaultSpeed * Time.deltaTime);
    18.     }
    19.  
    20.     public void Observe()
    21.     {
    22.         if (currentTarget != null)
    23.         {
    24. _targetRotation = Quaternion.LookRotation(currentTarget.GetComponent<ITargetable>().TargetableObj.transform.position - _camObj.transform.position);
    25.  
    26. _targetRotation.x = Mathf.Clamp(_targetRotation.x, -actorStatic.actorPitchClamp, actorStatic.actorPitchClamp);
    27.  
    28. _targetRotation.y = Mathf.Clamp(_targetRotation.y, -actorStatic.actorYawClamp, actorStatic.actorYawClamp);
    29.  
    30. _camObj.transform.rotation = Quaternion.RotateTowards(_camObj.transform.rotation, _targetRotation, actorStatic.actorAlternateSpeed * Time.deltaTime);
    31.         }
    32.     }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,749
    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.
     
  3. chatrat12

    chatrat12

    Joined:
    Jan 21, 2015
    Posts:
    122
    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.


    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class ConstrainedLookAtExample : MonoBehaviour
    4. {
    5.     [SerializeField] private float m_YawLimit = 90f;
    6.     [SerializeField] private float m_PitchLimit = 20f;
    7.  
    8.     // The head that will actually swivel
    9.     [SerializeField] private Transform m_CameraHead;
    10.     // Object head is tracking
    11.     [SerializeField] private Transform m_TrackedObject;
    12.  
    13.     private void Update()
    14.     {
    15.         // Can we look at target?
    16.         if (CanLookAt(m_TrackedObject.position, m_CameraHead.position))
    17.             // Look at target
    18.             m_CameraHead.LookAt(m_TrackedObject.position, Vector3.up);
    19.     }
    20.  
    21.     private bool CanLookAt(Vector3 targetPosition, Vector3 headPosition)
    22.     {
    23.         // Get direction from head to target
    24.         var direction = (targetPosition - headPosition).normalized;
    25.  
    26.         // Get yaw vector (Remove pitch)
    27.         var yawDirection = Vector3.ProjectOnPlane(direction, transform.up).normalized;
    28.  
    29.         // Set limits relative to results of dot products.
    30.         var yawLimit = Mathf.Sin((90 - m_YawLimit) * Mathf.Deg2Rad);
    31.         var pitchLimit = Mathf.Sin((90 - m_PitchLimit) * Mathf.Deg2Rad);
    32.  
    33.         // Check if we are within limits.
    34.         return Vector3.Dot(transform.forward, yawDirection) > yawLimit
    35.             && Vector3.Dot(direction,         yawDirection) > pitchLimit;
    36.     }
    37. }
     
  4. chatrat12

    chatrat12

    Joined:
    Jan 21, 2015
    Posts:
    122
    Math can be simplified by using the
    Vector3.Angle
    method.


    Code (CSharp):
    1. private bool CanLookAt(Vector3 targetPosition, Vector3 headPosition)
    2. {
    3.     // Get direction from head to target
    4.     var direction = (targetPosition - headPosition).normalized;
    5.  
    6.     // Get yaw vector (Remove pitch)
    7.     var yawDirection = Vector3.ProjectOnPlane(direction, transform.up).normalized;
    8.  
    9.     return Vector3.Angle(yawDirection, transform.forward) < m_YawLimit
    10.         && Vector3.Angle(direction, yawDirection) < m_PitchLimit;
    11. }
     
  5. TheStarWarrior

    TheStarWarrior

    Joined:
    Jan 6, 2020
    Posts:
    2
    Hey, that angle method worked great Chatrat! And I really appreciate the help from both of you guys.