Угол Эйлера кватерниона становится отрицательным без причины

Я делаю цикл день-ночь для своей игры. То, как я это делаю, заключается в том, что у меня есть DateTime, к которому я добавляю секунды каждую секунду. У меня также есть целые минуты, которые в основном (DateTime.hours * 60 + DateTime.minutes).

Я хочу повернуть солнце на основе переменной минут. Окончательное уравнение - Quaternion.Euler (минуты / 4, 0, 0). Все это работает до тех пор, пока DateTime не достигнет полудня в этот момент, солнце не начнет глючить, уходя в отрицательную сторону, а затем возвращается к правильному вращению до полуночи.

В какой-то момент инспектор не показывает те же значения, что показывает Debug.Log солнечной гнили.

Вот видео, показывающее, что происходит. Извините за плохое качество https://thewikihow.com/video__Gtb1h8feGs

Я где-то видел, что это может быть связано с замком карданного подвеса. Я не лучший, когда дело доходит до кватернионов, поэтому я могу говорить чепуху. В любом случае, вот мой код

 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);
        }
    }
}

Любая помощь приветствуется

Обратите внимание, что поворот на 345,25 градуса аналогичен повороту на -14,75 градуса...

Daevin 16.09.2022 20:01

Единственное, что здесь выглядит иначе, это то, что вы выборочно вызываете функцию, которой не поделились с нами — onDayTimeChange. Что это такое, и что происходит, когда вы его проходите DayTime.Night? Этого нет в фрагменте, который вы разместили.

Chuck 16.09.2022 20:06

Изменение onDayTimeChange вызывается в IEnumerator TimeClock() ближе к концу.

Snapper 16.09.2022 20:08

Я вижу, что это называется. Я не вижу, что он делает.

Chuck 16.09.2022 20:09

Это совпадение, что солнце начинает вести себя странно в полдень, а в вашем методе TimeClock() блок if (date.Hour == 12) — единственный, у которого нет логики помимо dayTime = [...] и Deblug.Log?

Daevin 16.09.2022 20:12

@Chuck Всякий раз, когда меняется состояние дня (утро, полдень, вечер, ...), я называю это. Я не использовал его ни в этом фрагменте, ни где-либо еще. В своем предыдущем комментарии вы сказали, что вызываете функцию, но onDayTimeChange — это событие.

Snapper 16.09.2022 20:13

@Snapper «Всякий раз, когда меняется состояние дня (утро, полдень, вечер, ...), я называю это». это не совсем правильно; как я уже сказал, вы не вызываете его внутри блока else if (date.Hour == 12), как с любым другим значением DayTime.

Daevin 16.09.2022 20:16

Я только сейчас это заметил. Спасибо что подметил это. Это просто ошибка на моей стороне. После исправления солнце кажется таким, что начинает глючить в 6 утра.

Snapper 16.09.2022 20:18

@Snapper также обратите внимание, что в ваших чеках у вас есть if (date.Hour >= 0 && date.Hour <= 12), а затем else if (date.Hour == 12)... но вы уже проверяете date.Hour <= 12, поэтому он никогда не достигнет date.Hour == 12, и вы никогда не установите dayTime = DayTime.Noon.

Daevin 16.09.2022 20:18

Должно быть, я закодировал это поздно ночью. После исправления всего этого солнце все еще глючит.

Snapper 16.09.2022 20:21
In your previous comment you said calling a function but onDayTimeChange is an event -.- Я пытаюсь помочь тебе, приятель. Есть ли у него подписчики? Эти подписчики что-нибудь делают?
Chuck 16.09.2022 20:28

Я знаю об этом, извините, если это показалось немного резким, но это вовсе не было намерением. Отвечая на ваш вопрос, нет, у него нет подписчиков. Просто там для меня, чтобы использовать в будущем

Snapper 16.09.2022 20:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
12
96
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Единственное другое исправление, о котором я могу думать, заключается в том, что иногда Unity будет изменять преобразование кватерниона-Эйлера, так что две оси смещаются, чтобы удерживать другую ось в некоторых пределах. Я думаю, что эти пределы могут быть -90...90, но здесь я не уверен.

Я подозреваю, что проблема, с которой вы столкнулись, заключается в том, что вы пытаетесь установить код, опрашивая текущий поворот и обновляя один угол. Если единица преобразовала, например, <91, 0, 0> в <1, 90, 90> или что-то подобное, и вы берете это вращение и устанавливаете следующий угол x равным 92, тогда вы устанавливаете <92, 90, 90> вместо предполагаемых <92, 0, 0>. Это будет не то, что вы имеете в виду, и вы закончите тем, что Unity повторно изменит координаты, чтобы снова быть в пределах границ.

Два возможных решения здесь:

  1. Применять только добавочные изменения или
  2. Кэшируйте повороты y/z в Awake и используйте их.

Я настоятельно рекомендую вариант (2), потому что, если вы когда-либо применяете только дельты, вы обнаружите, что ограничения с плавающей запятой и другие ошибки округления приведут вас к тому, что ваше солнце находится в неправильном положении во время день.

Вот предлагаемое исправление для вашего скрипта:

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);
    };
}

Это должно гарантировать, что ваши повороты останутся такими, как вы ожидаете, даже если внутреннее преобразование Unity из кватерниона в Эйлера перевернет оси на вас (потому что вы не начинаете с этих значений).

Другие вопросы по теме