Rupture
Rupture

Reputation: 110

Unity Transform random rotation between specific angles

Ok, I need the transforms divided into specific groups randomly rotating between given angles only on the local x axis. I don't understand Quaternions and transform.localEulerAngles seems to only work with angles in <0, 360> range. All my angle boundaries are in <-180, 180>. It all seems to work for a while and then the rotations get stuck around 90 and -90 for some reason...

Here's the code:

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

//this is the editor interface into which I input the demands
[System.Serializable]
struct Transform_Angle_VP
{
    public float angle1;
    public float angle2;
    public float max_speed;
    public Transform[] transforms;    
}

public class LeftCollectionDarkMovement : MonoBehaviour {

    public float speed_change;
    [SerializeField] Transform_Angle_VP[] transform_Angles;
    Dictionary<Transform, float[]> TransAngs = new Dictionary<Transform, float[]>();

    // Use this for initialization
    void Awake()
    {
        //rewrite transforms and their angle limits into dictionary to keep seperate track of each
        foreach (Transform_Angle_VP T_A_VP in transform_Angles)
        {
            foreach (Transform trans in T_A_VP.transforms)
            {
                ///float[0:min_angle, 1:max_angle, 2:target_angle, 3:current_speed, 4:target_speed, 5:max_speed]
                TransAngs[trans] = new float[] { T_A_VP.angle1, T_A_VP.angle2, Random.Range(T_A_VP.angle1, T_A_VP.angle2), 0, Random.Range(T_A_VP.max_speed / 5, T_A_VP.max_speed), T_A_VP.max_speed };
            }
        }
    }

    // Update is called once per frame
    void Update () {
        float dt = Time.deltaTime;
        foreach(Transform trans in TransAngs.Keys)
        {
            var parameters = TransAngs[trans];            
            //move speed towards target
            parameters[3] = Mathf.Min(parameters[4], parameters[3] + dt * speed_change);

            //translate localEulerAngles to negatives if needed
            var rot_x = trans.localEulerAngles.x;
            if (rot_x > 180) rot_x -= 360;

            //calculate rotation of target
            var target_rot = Mathf.MoveTowards(rot_x, parameters[2], parameters[3] * dt);
            target_rot -= rot_x;

            //apply rotation
            trans.localEulerAngles += new Vector3(target_rot, 0, 0);

            //if at target : choose new target and speed
            rot_x = trans.localEulerAngles.x;
            if (rot_x > 180) rot_x -= 360;
            if (Mathf.Abs(rot_x - parameters[2]) < 1)
            {
                parameters[2] = Random.Range(parameters[0], parameters[1]);
                parameters[4] = Random.Range(parameters[5]/5, parameters[5]);            
            }
        }
    }
}

Upvotes: 2

Views: 1691

Answers (1)

Tobias K.
Tobias K.

Reputation: 561

The documentation on localEulerAngles states that they should not be incremented and that Transform.Rotate should be used instead. The reason for this that Unity uses quaternions internally and converts to Euler angles on request. You should not rely on localEulerAngles to return consistent values as they are not the underlying representation. In fact localEulerAngles is often not equal to what is shown in the inspector.

localEulerAngles tends to switch between two different representations of the rotation at the +-90 degree mark. This is why the rotation gets stuck there.

Instead i would suggest storing the current rotation in your parameters and then update it accordingly. This way you have a value you can rely on and can use any range of angles you desire.

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

//this is the editor interface into which I input the demands
[System.Serializable]
struct Transform_Angle_VP
{
    public float angle1;
    public float angle2;
    public float max_speed;
    public Transform[] transforms;
}

public class LeftCollectionDarkMovement : MonoBehaviour
{

    public float speed_change;
    [SerializeField] Transform_Angle_VP[] transform_Angles;
    Dictionary<Transform, float[]> TransAngs = new Dictionary<Transform, float[]>();

    // Use this for initialization
    void Awake()
    {
        //rewrite transforms and their angle limits into dictionary to keep seperate track of each
        foreach (Transform_Angle_VP T_A_VP in transform_Angles)
        {
            foreach (Transform trans in T_A_VP.transforms)
            {
                ///float[0:min_angle, 1:max_angle, 2:target_angle, 3:current_speed, 4:target_speed, 5:max_speed, 6: current_rotation]
                TransAngs[trans] = new float[] { T_A_VP.angle1, T_A_VP.angle2, Random.Range(T_A_VP.angle1, T_A_VP.angle2), 0, Random.Range(T_A_VP.max_speed / 5, T_A_VP.max_speed), T_A_VP.max_speed, 0 };
            }
        }
    }

    // Update is called once per frame
    void Update()
    {
        float dt = Time.deltaTime;
        foreach (Transform trans in TransAngs.Keys)
        {
            var parameters = TransAngs[trans];
            //move speed towards target
            parameters[3] = Mathf.Min(parameters[4], parameters[3] + dt * speed_change);

            //calculate rotation of target
            var target_rot = Mathf.MoveTowards(parameters[6], parameters[2], parameters[3] * dt);
            target_rot -= parameters[6];

            //apply rotation
            trans.Rotate(Vector3.right, target_rot, Space.Self);
            parameters[6] += target_rot;

            if (Mathf.Abs(parameters[6] - parameters[2]) < 1)
            {
                parameters[2] = Random.Range(parameters[0], parameters[1]);
                parameters[4] = Random.Range(parameters[5] / 5, parameters[5]);
            }
        }
    }
}

Upvotes: 1

Related Questions