Why the clamping is not working good ?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateToTarget : MonoBehaviour
{
    // The target marker.
    public Transform target;

    // Angular speed in radians per sec.
    public float speed = 1.0f;

    void Update()
    {
        // Determine which direction to rotate towards
        Vector3 targetDirection = target.position - transform.position;

        // The step size is equal to speed times frame time.
        float singleStep = speed * Time.deltaTime;

        // Rotate the forward vector towards the target direction by one step
        Vector3 newDirection = Vector3.RotateTowards(transform.forward, targetDirection, singleStep, 0.0f);

        newDirection.x = ClampAngle(newDirection.x, 60f, -60f);

        // Calculate a rotation a step closer to the target and applies rotation to this object
        transform.rotation = Quaternion.LookRotation(newDirection);
    }

    float ClampAngle(float angle, float from, float to)
    {
        if (angle < 0f) angle = 360 + angle;
        if (angle > 180f) return Mathf.Max(angle, 360 + from);
        return Mathf.Min(angle, to);
    }
}

By adding the line :

newDirection.x = ClampAngle(newDirection.x, 60f, -60f);

It’s making the object to rotate very very fast nonstop from side to side like crazy.
I want to clamp the rotation on the two axis x and y at this angles : Z is optional :

Rotation : X between 60 and -60
Rotation : Y between 100 and -100
Rotation : Z between 60 and -60 ( Only if needed optional )

First of all, you are trying to treat newDirection.x as if it were an angle, and it is not. It is the x component of a unit vector pointed in the direction you want to face. The corresponding angles would be

Vector3 eulerAngles = Quaternion.LookRotation(newDirection).eulerAngles;

Secondly, your ClampAngle function is entirely wrong. Try taking some example numbers (e.g. suppose that you called ClampAngle(1, 60, -60)) and then work through the function in your head (or on some scratch paper) one line at a time, calculating what will happen. Pretty sure it’s not what you’re expecting.

For your current use case, I believe it is always going to return -60. Your calls to max and min are backwards (but this is hard to spot because your arguments have ambiguous names), you are trying to standardize your angle to the range of (0, 360) but you are passing in arguments that aren’t in that range so they aren’t comparable, and I don’t even know why you’re adding 360 to “from”.

The proper way to clamp an angle would be to convert everything to the same standardized range (say, -180 to 180) and then apply the normal Mathf.Clamp function as if it were just a plain number. (And if you want to be completely safe about converting angles to a standardized range, you need to worry about weird cases like ten thousand degrees, not just small negative numbers.)

Thirdly, clamping rotations is inherently subtle due to the fact that euler angles aren’t linearly independent like coordinate axes are, so you actually have to be pretty careful to avoid weird bugs in special cases. For instance, the euler angles (180, 0, 0) and (0, 180, 180) are actually the same rotation, so just clamping the X value to be in the range (-60, 60) doesn’t necessarily stop you from getting to a rotation that’s equivalent to (180, 0, 0).

The simplest way to do this would probably be to store the euler angles in your own variables from frame-to-frame (don’t rely on the ones returned by the Transform, they can change because they are converted to a Quaternion and then back), apply any updates first to your variables, and then clamp the numbers in your variables before you apply them to the Transform. This should be “safe” as long as you are clamping to a small enough range that there is at most one legal representation of any orientation, which is true for the example clamps you listed.

2 Likes

I tried this way but it still rotating also to the back facing backward.
Since the target in this case a cube is rotating nonstop around the player when the target is behind the player the player head rotate backward. I want to clamp it so it will stay only facing forward area and not to rotate to the back too.

This is the target rotating script attached to the cube :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateAround : MonoBehaviour
{
    // Add this script to rotating object
    [Header("Gameobject that object/s will rotate around as center point")]
    public GameObject centerObj;//to get the position in worldspace to which this gameObject will rotate around.

    [Header("The axis by which it will rotate around")]
    public Vector3 axis;//by which axis it will rotate. x,y or z.

    [Header("Angle covered per update")]
    public float angle; //or the speed of rotation.

    public float upperLimit, lowerLimit, delay;// upperLimit & lowerLimit: heighest & lowest height; 
    private float height, prevHeight, time;//height:height it is trying to reach(randomly generated); prevHeight:stores last value of height;delay in radomness; 

    // Update is called once per frame
    void Update()
    {
        //Gets the position of your 'Center' and rotates this gameObject around it by the 'axis' provided at speed 'angle' in degrees per update 
        transform.RotateAround(centerObj.transform.position, axis, angle);
        time += Time.deltaTime;
        //Sets value of 'height' randomly within 'upperLimit' & 'lowerLimit' after delay 
        if (time > delay)
        {
            prevHeight = height;
            height = Random.Range(lowerLimit, upperLimit);
            time = 0;
        }
        //Mathf.Lerp changes height from 'prevHeight' to 'height' gradually (smooth transition) 
        transform.position = new Vector3(transform.position.x, Mathf.Lerp(prevHeight, height, time), transform.position.z);
    }
}

I want to make like a lookat but only for the front area and not to the back when the target is behind the player.