Beks_Omega
Beks_Omega

Reputation: 454

How to Limit (clamp) Y axis Rotation for transform.rotatearound Unity

I have a camera that I want to rotate around a point (0,0,0) in all directions, but I want to put a clamp on it so that it can't go too far above or below the point. I have seen this question answered for the left and right directions before but never for the vertical one.

I have tried converting the code from these two questions (that basically say the same thing) to work in the vertical direction, but it bugs out at some points along the rotation, and I can't figure out why.

First Question, Second Question

And this is how I tried to convert it:

//how much we want to rotate by this frame
float rotX = Input.GetAxis("Mouse X") * rotSpeed;
float rotY = Input.GetAxis("Mouse Y") * rotSpeed; //(before clamping)

//find current direction
Vector3 currentDirection = transform.position - Vector3.zero;

//find current angle between basis for clamp & where we are now
float angle = Vector3.Angle(Vector3.forward, currentDirection);

 //finds out if it's up or down
if (Vector3.Cross(Vector3.forward, currentDirection).x < 0) angle = -angle; 

 //find out how much you can move without violating limits
 float newAngle = Mathf.Clamp(angle + rotY, yMinLimit, yMaxLimit);

//grabs how much you are allowed to move the angle from the current angle
rotY = newAngle - angle;

//spinning the garden
transform.RotateAround(Vector3.zero, Vector3.up, rotX);
 transform.RotateAround(Vector3.zero, transform.TransformDirection(Vector3.right), -rotY); //vertical rotation

If anyone knows of the correct way to make this work for the Y axis, or a different way to clamp the vertical rotation, I would be super excited to hear it! Ty!

Upvotes: 2

Views: 8253

Answers (1)

Magrones
Magrones

Reputation: 433

I have a class here that do exactly what you want. It rotates a camera around a target and clamps the Y rotation. It uses the left button to rotate and the scroll press button to translate the target. You can edit it to adjust to your specific needs - you might want to change the target to a Vector3 so you can set it to (0,0,0) without the need of an object. Hope it helps.

using UnityEngine;


public class RotateAroundCamera : MonoBehaviour
{
    Camera cam;
    public bool isControlable;
    private Vector3 screenPoint;
    private Vector3 offset;
    public Transform target;
    public float distance = 5.0f;
    public float xSpeed = 50.0f;
    public float ySpeed = 50.0f;

    public float yMinLimit = -80f;
    public float yMaxLimit = 80f;

    public float distanceMin = .5f;
    public float distanceMax = 15f;

    public float smoothTime = 2f;

    public float rotationYAxis = 0.0f;
    float rotationXAxis = 0.0f;

    float velocityX = 0.0f;
    float velocityY = 0.0f;
    float moveDirection = -1;

    public void SetControllable(bool value)
    {
        isControlable = value;
    }

    // Use this for initialization
    void Start()
    {
        cam = GetComponentInChildren<Camera>();
        Vector3 angles = transform.eulerAngles;
        rotationYAxis = (rotationYAxis == 0) ? angles.y : rotationYAxis;
        rotationXAxis = angles.x;

        Rigidbody rigidbody = GetComponent<Rigidbody>();

        // Make the rigid body not change rotation
        if (rigidbody)
        {
            rigidbody.freezeRotation = true;
        }
    }

    void LateUpdate()
    {
        if (target)
        {
            if (Input.GetMouseButton(1) && isControlable)
            {
                velocityX += xSpeed * Input.GetAxis("Mouse X") * 0.02f;
                velocityY += ySpeed * Input.GetAxis("Mouse Y") * 0.02f;
            }


            if (Input.GetMouseButton(2) && isControlable)
            {
                Vector3 curScreenPoint = new Vector3(moveDirection*Input.mousePosition.x, moveDirection*Input.mousePosition.y, screenPoint.z);

                Vector3 curPosition = cam.ScreenToWorldPoint(curScreenPoint) + offset;
                target.transform.position = curPosition;
            }

            if (Input.GetKeyDown(KeyCode.R) && isControlable)
            {
                target.transform.position = Vector3.zero;
            }

            if (Input.GetKeyDown(KeyCode.T) && isControlable)
            {
                moveDirection *= -1;
            }

            if (isControlable)
            {
                distance -= Input.GetAxis("Mouse ScrollWheel");

                if (distance > distanceMax)
                {
                    distance = distanceMax;
                }
                else if (distance < distanceMin)
                {
                    distance = distanceMin;
                }
            }

            rotationYAxis += velocityX;
            rotationXAxis -= velocityY;

            rotationXAxis = ClampAngle(rotationXAxis, yMinLimit, yMaxLimit);

            Quaternion fromRotation = Quaternion.Euler(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, 0);
            Quaternion toRotation = Quaternion.Euler(rotationXAxis, rotationYAxis, 0);
            Quaternion rotation = toRotation;

            Vector3 negDistance = new Vector3(0.0f, 0.0f, -distance);
            Vector3 position = rotation * negDistance + target.position;

            transform.rotation = rotation;
            transform.position = position;

            velocityX = Mathf.Lerp(velocityX, 0, Time.deltaTime * smoothTime);
            velocityY = Mathf.Lerp(velocityY, 0, Time.deltaTime * smoothTime);

            screenPoint = cam.WorldToScreenPoint(target.transform.position);
            offset = target.transform.position - cam.ScreenToWorldPoint(new Vector3(moveDirection*Input.mousePosition.x, moveDirection*Input.mousePosition.y, screenPoint.z));
        }

    }

    public static float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360F)
            angle += 360F;
        if (angle > 360F)
            angle -= 360F;
        return Mathf.Clamp(angle, min, max);
    }
}

Upvotes: 3

Related Questions