Home > Back-end >  Quaternion euler angle going into negative for no reason
Quaternion euler angle going into negative for no reason

Time:09-17

I'm making a day-night cycle for my game. The way I do it, is that I have a DateTime to which I add seconds each second. I also have an integer minutes which is basically (DateTime.hours * 60 DateTime.minutes).

I want to rotate the sun based on the minutes variable. The final equation is Quaternion.Euler(minutes / 4, 0, 0). All of that works until the DateTime reaches noon at this point, the sun starts glitching out going into negative then back to the right rotation until midnight.

At some point the inspector isn't showing the same values that a Debug.Log of the sun's rot shows. enter image description here

Here's a video showing what happens. Sorry for the poor quality https://youtu.be/_Gtb1h8feGs

I've seen somewhere that it might be due to the Gimbal lock. I'm not the best when it comes to quaternions so I might be talking nonsense. Anyways, here's my code

 public enum DayTime
{
    Morning,
    Noon,
    AfterNoon,
    Evening,
    Night
}

public class MoonTime : MonoBehaviour
{
    public DateTime date = new DateTime(2023, 1, 1, 0, 0, 0);

    [HideInInspector]
    public float minutes = 0;
    public float dayLength = 4;

    public float smoothness = 4.5f;

    [HideInInspector]
    public DayTime dayTime;

    public delegate void TimeChange(DateTime date);
    public event TimeChange onTimeChange;

    public delegate void DayTimeChange(DayTime dayTime);
    public event DayTimeChange onDayTimeChange;


    public Material daySkyBox;
    public Material nightSkyBox;

    public Light Sun;
    public Light Moon;

    private void Awake()
    {
        StartCoroutine(TimeClock());

        onTimeChange  = (d) =>
        {
            var sunCurrentRot = Sun.transform.rotation.eulerAngles;

            Debug.Log("What rot is should be "   Quaternion.Euler(minutes / 4, sunCurrentRot.y, sunCurrentRot.z).eulerAngles);
            Sun.transform.rotation = Quaternion.Euler(minutes / 4, sunCurrentRot.y, sunCurrentRot.z);
            Debug.Log("What is it "   Sun.transform.rotation.eulerAngles);

            //var moonCurrentRot = Moon.transform.rotation.eulerAngles;

            //Moon.transform.rotation = Quaternion.Euler((minutes * 0.25f)   180, moonCurrentRot.y, moonCurrentRot.z);
        };
    }

    private void Update()
    {
    }

    public IEnumerator TimeClock()
    {
        while (true)
        {
            yield return new WaitForSecondsRealtime(1 / smoothness);

            float finalValue = 1440 / (dayLength * 60) / smoothness;

            date = date.AddSeconds(finalValue);

            minutes = (date.Hour * 60)   date.Minute;


            if (date.Hour >= 0 && date.Hour < 12)
            {
                if (dayTime != DayTime.Morning)
                {
                    dayTime = DayTime.Morning;
                    Debug.Log("Morning");
                    onDayTimeChange?.Invoke(DayTime.Morning);
                }
            }
            else if (date.Hour == 12)
            {
                if (dayTime != DayTime.Noon)
                {
                    dayTime = DayTime.Noon;
                    Debug.Log("Noon");
                    onDayTimeChange?.Invoke(DayTime.Noon);
                }
            }
            else if(date.Hour > 12 && date.Hour <= 15)
            {
                if (dayTime != DayTime.AfterNoon)
                {
                    dayTime = DayTime.AfterNoon;
                    Debug.Log("After Noon");
                    onDayTimeChange?.Invoke(DayTime.AfterNoon);
                }
            }
            else if (date.Hour > 15 && date.Hour <= 17)
            {
                if (dayTime != DayTime.Evening)
                {
                    dayTime = DayTime.Evening;
                    Debug.Log("Evening");
                    onDayTimeChange?.Invoke(DayTime.Evening);
                }
            }
            else
            {
                if (dayTime != DayTime.Night)
                {
                    dayTime = DayTime.Night;
                    Debug.Log("Night");
                    onDayTimeChange?.Invoke(DayTime.Night);
                }
            }

            onTimeChange?.Invoke(date);

            Debug.Log(date);
        }
    }
}

Any help is appreciated

CodePudding user response:

Only other fix I can think of is that sometimes Unity will alter the Quaternion-Euler conversion, such that two axes shift to keep another axis within some limits. I think these limits might be -90...90, but I'm not positive here.

I suspect the problem you're having is when you try to set the code by polling the current rotation and updating one angle. If unity has converted, for example, <91, 0, 0> to <1, 90, 90> or something similar, and you take that rotation and set the next x-angle to 92, then you wind up setting <92, 90, 90> instead of your intended <92, 0, 0>. These won't be the same as what you mean, and you'll wind up with Unity re-changing the coordinates to be within bounds again.

Two possible solutions here:

  1. Apply incremental changes only, or
  2. Cache the y/z rotations in Awake and use those.

I would strongly suggest option (2), because if you're only ever applying deltas then you'll find that floating-point limitations and other rounding errors will get you to the point that your sun is in the wrong position for the time of day.

Here's the suggested fix to your script:

private float sunY;
private float sunZ;
private void Awake()
{
    StartCoroutine(TimeClock());
    sunY = Sun.transform.rotation.eulerAngles.y;
    sunZ = Sun.transform.rotation.eulerAngles.z;
    onTimeChange  = (d) =>
    {
        Debug.Log("What rot is should be "   Quaternion.Euler(minutes / 4, sunY, sunZ).eulerAngles);
        Sun.transform.rotation = Quaternion.Euler(minutes / 4, sunY, sunZ);
        Debug.Log("What is it "   Sun.transform.rotation.eulerAngles);

        //var moonCurrentRot = Moon.transform.rotation.eulerAngles;

        //Moon.transform.rotation = Quaternion.Euler((minutes * 0.25f)   180, moonCurrentRot.y, moonCurrentRot.z);
    };
}

This should ensure your rotations stay the way you expect even if Unity's internal Quaternion-to-Euler conversion flips axes on you (because you're not starting from those values).

  • Related