Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question How do I limit the rotation of a camera orbiting an empty GameObject?

Discussion in 'Input System' started by Sandtile, Sep 7, 2020.

  1. Sandtile


    Dec 9, 2018
    -Character (GameObject)-> has an idependent controller script so it rotates independently of the camera's rotation. No problem.
    -Camera Rotator (empty Gameobject)-> has a camera rotation script based on the input of the mouse.
    -Main Camera

    Sorry, I am a beginner in Unity :((joined in 2018 but started learning this month), and programming.

    This is the code component of the Empty GameObject called Camera Rotator:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    5. public class CameraRotScript : MonoBehaviour
    6. {
    8.     float rotX;
    12.     float rotspeedY = 100.0f;
    13.     float rotspeedX = 100.0f;
    14.     // Start is called before the first frame update
    15.     void Start()
    16.     {
    20.     }
    22.     // Update is called once per frame
    23.     void Update()
    24.     {
    26.         rotX = transform.eulerAngles.x;
    28.         //to avoid problems with negative rotation values, I add +360 to a negative value.
    29.         if (rotX < 0)
    30.         {
    31.             rotX += 360;
    32.         }
    37.         if (Input.GetMouseButton(1))
    38.         {
    39.             transform.Rotate(0, Input.GetAxis("Mouse X") * rotspeedY * Time.deltaTime, 0);
    40.         }
    46.         if (Input.GetMouseButton(1))
    47.         {
    48.             if (rotX > 310 || rotX < 40)
    49.             {
    51.                 transform.Rotate(Input.GetAxis("Mouse Y") * rotspeedX * Time.deltaTime, 0, 0);
    52.             }
    53.         }
    56.     }
    60. }
    (^ I do the same for the rotation around the Y axis but I don't want to limit it, so it's out of question.)

    This code is to rotate the Camera Rotator so its child, the Main Camera(where I have already inserted a
    script to LookAt the rotator), also gets rotated. The problem is, I want to limit its rotation to -60 and 60.
    I have tried many different ways to do that. The answers of similar questions the internet are a bit confusing for me, it's like they are answering people who have been programming for some time on the engine.

    My most successful but still not desired result was obtained by adding +360 in case the float transform.eulerAngles.x<0, and using conditions of transform.eulerAngles.x>300 and transform.eulerAngles.x<60 for the camera to be able to be rotated. But the camera just freezes in the boundaries... and another problem: if I shake the mouse too much, its "deltas" will be so big that the X rotation will get to something like 100 or even more, which is undesirable.

    Is the problem the hierarchy? Or the way I'm using the input, so the shaking throws a very high value of the Mouse Y input? What about the boundaries freezing forever instead of just limiting? I don't know if this is common with beginners but at this rate I'm starting to think I'm just throwing random stuff at my code until it somehow works... if I need to rewrite THE WHOLE CODE, I WILL, but, please, help! :confused:
  2. Sparot


    Aug 23, 2020
    Hey, I'll see if I can help here.

    If I have this right, you're trying to make a simple script that orbits the camera around a focal point, but you're having issues clamping the camera along the up and down axes relative to your object. You're also running into an issue with the camera getting stuck. I also noticed this code causes the camera to lean left and right.

    The primary issue would be using a game object as your focal point. If your camera were in a fixed position, and you were only rotating on one axis, this would be fine. However, once you introduce more than one axis, you start to run into weird euler issues. You're actually rotating on all three axes. This is why your deltas and your rotX check fails, because you're also rotating on the Z axis unknowingly.

    I would recommend using math to calculate the position of the camera manually. You also mentioned that you looked up online how to do this, and the answers were a bit confusing. After approaching the problem several different ways, I tried to come up with the simplest method I could think of. Since it's frustrating looking for answers to a problem and all you get is really complicated or difficult to understand solutions, I'll just post the code instead and explain it after.

    Code (CSharp):
    2. public class CameraRotScript : MonoBehaviour
    3. {
    4.     //The focal point of our camera.
    5.     public Transform FocalPoint = null;
    6.     //The distance from the object we want to position our camera.
    7.     public float FocalDistance = 10.0f;
    8.     //The angle left and right relative to the object.
    9.     public float AngleX = 0.0f;
    10.     //The angle up and down relative to the object.
    11.     public float AngleY = 0.0f;
    12.     //The clamping values for the lower and upper angles of our y angle.
    13.     public Vector2 AngleYClamp = new Vector2(-60.0f, 60.0f);
    15.     public float rotspeedY = 100.0f;
    16.     public float rotspeedX = 100.0f;
    18.     // Update is called once per frame
    19.     void Update()
    20.     {
    21.         //If the right mouse button is pressed.
    22.         if (Input.GetMouseButton(1))
    23.         {
    24.             //Move our angleX by the mouse X's value.
    25.             AngleX += Input.GetAxis("Mouse X") * rotspeedX * Time.deltaTime;
    26.             //Move our angleY by the mouse Y's value.
    27.             AngleY += Input.GetAxis("Mouse Y") * rotspeedY * Time.deltaTime;
    29.             //Clamp AnglY using Mathf.Clamp and the AngleYClamps.
    30.             AngleY = Mathf.Clamp(AngleY, AngleYClamp.x, AngleYClamp.y);
    32.             //Prevent excessive rollover, it could cause messy results later.
    33.             if (AngleX > 360.0f)
    34.                 AngleX -= 360.0f;
    35.             if (AngleX < 0.0f)
    36.                 AngleX += 360.0f;
    38.             //Prevent excessive rollover, it could cause messy results later.
    39.             if (AngleY > 360.0f)
    40.                 AngleY -= 360.0f;
    41.             if (AngleY < 0.0f)
    42.                 AngleY += 360.0f;
    43.         }
    45.         CalculateCameraPosition();
    46.     }
    48.     // Calculate a new camera position based on our inputs.
    49.     void CalculateCameraPosition()
    50.     {
    51.         if (FocalPoint != null)
    52.         {
    53.             //Calculate a camera position that's a certain distance from zero.
    54.             Vector3 tCameraPosition = -Vector3.forward * FocalDistance;
    56.             //Rotate that position along the x/z axis by the AngleY the user has specified. This is a rotation relative to the origin.
    57.             tCameraPosition = Quaternion.Euler(AngleY, 0.0f, 0.0f) * tCameraPosition;
    59.             //Rotate the position along the y axis by the AngleX the user has specified. This is a rotation relative to the origin.
    60.             tCameraPosition = Quaternion.Euler(0.0f, AngleX, 0.0f) * tCameraPosition;
    62.             //With our new rotated position calculated, offset it by the focal point's position. That way, if something moves the focal point, we'll keep up with it.
    63.             transform.position = FocalPoint.transform.position + tCameraPosition;
    65.             //Tell the transform of the camera to look at the object. This is a handy function that does some work for us.
    66.             transform.LookAt(FocalPoint);
    67.         }
    68.     }
    69. }

    The idea is pretty simple. You want to position your camera relative to a focal point, a Transform basically. You solve each rotation one step at a time.

    1. You take the position of that transform and push it away by a certain distance(forward * focal distance).
    2. Rotate this position using Quaternions. Quaternions are simply representations of rotations, but you only need to worry about the angle value you input in. Multiplying a position or vector by a quaternion rotates it relative to the origin. Quaternions always rotate before a vector(Quaternion * Vector).
    3. Once the camera is positioned, call transform.LookAt to face the camera towards your object. It's a handy function that does a lot of math for us.

    This should give you a much more stream-lined rotation, as it's calculated every frame and is solved using quaternion math. You don't have to keep up with much using this method. All you have to do is assign the focal point on the script in the inspector, and it should snap to it and start working immediately.

    You should also try using "public" or "[SerializeField]" to expose data members to the Unity editor. Since all of these values are public/serialized, the inspector will let you tweak the numbers while the game is running. With this script, trying changing the focal distance or the angles while the game is running. You'll also see the angle values change in the inspector as you move with the mouse.

    Good luck, hope this helps!
    Sandtile likes this.
  3. Sandtile


    Dec 9, 2018
    Thanks for the help! :) But for some reason, the camera's rotation returns to X=60 when I reach the value of 0

    Edit: nvm, fixed it by removing the if(AngleY<0){AngleY += 360} thing. As you clamped it to on max 60, it was returning to this value, as 300 is much bigger than 60. Thank you very much! I see a big part of my mistakes were caused by a certain difficulty on understanding the reference and value stuff. ;)
    Last edited: Sep 10, 2020