Що відбувається, коли Time.time стає дуже великим в Unity?


37

Як сказано тут :

Time.time

Час на початку цього кадру (лише для читання). Це час у секундах від початку гри.

І як я знаю, час зберігається в поплавці. Отже, моє запитання - що буде, коли значення часу стане дуже великим? Чи може воно переповнитись? Чи втратить це точність і спричинить помилки? Чи гра просто вийде з ладу чи що?



5
@AlexandreVaillancourt Я думаю, що тут є корисне питання, якщо ми інтерпретуємо це як "Чи є проблеми, коли Time.time стає дуже великим?" на відміну від зосередження буквально на «переповненні». Я редагував це запитання в цих рядках, щоб спробувати вирішити ваші відгуки.
DMGregory

2
@DMGregory Але на запитання було відповідено і вже прийнято ... Я згоден, що ваші зміни виправдають корисніше питання, трохи пізніше.
MichaelHouse

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

Відповіді:


62

Існує реальний ризик втрати точності в часі від використання одноточного поплавця .

Він зберігатиме точність до найближчої секунди протягом 97 днів . Але в іграх ми часто піклуємося про точність за порядком тривалості кадру.

Одноточне значення часу поплавця в секундах починає втрачати точність в мілісекундах приблизно через 9 годин .

Це вже не виходить за межі можливості для одного ігрового сеансу або залишити гру, що працює в режимі "AFK", поки ви працюєте / навчаєтесь або спите. (Це одна з причин, чому загальний спосіб перевірити гру - це запустити її протягом ночі і перевірити, чи вона все ще грає правильно вранці)

На сучасних консолях, де ігри часто призупиняються та відновлюються між ігровими сесіями, а не закриваються цілком, не "несподівано", щоб "одиничний сеанс" з точки зору гри перевищував 9 годин роботи, навіть якщо вони гралися в короткий час.

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

Оскільки fixedDeltaTimeзберігає те саме значення, яке ви встановили, замість того, щоб динамічно змінюватися від кадру до кадру, ви також можете помістити поведінку, залежну від часу, у FixedUpdate, щоб отримати більш гарантії послідовності. deltaTimeавтоматично поверне відповідну фіксовану дельту в цих методах. Незважаючи на те, що між оновленнями фіксованого та кадру може бути частота збивання, ви можете це згладити за допомогою інтерполяції .

Чого ви хочете уникнути, це обчислення тривалості, віднімання однієї часової позначки від іншої . Після години гри це може призвести до катастрофічного скасування, що призведе до набагато меншої точності, ніж ви станете рано в дорозі. Якщо порівняння часових позначок важливе для ваших систем, ви можете генерувати власне значення часу з більшою роздільною здатністю, використовуючи інші методи, наприклад System.Diagnostics.Stopwatchзамість Time.time.


Unreal також вирішує виставити ігровий час як одноточний поплавок, як у коді, так і в кресленнях . Ви можете обійти його за допомогою часових позначок в режимі реального часу і робити свій власний розширення часу, як ви робили в Unity. (Просто слідкуйте за тим, що тип часової позначки Unreal базується на 32-бітовій епосі ). Якщо ви вирішите, розширення існують для C # у Unreal.
DMGregory

Для [мс] ви починаєте втрачати точність приблизно в діапазоні 16 484 - 32 768. Тобто ви можете втратити точність через 4h30min , через 9h ви точно не можете цьому довіритися.
xmedeko

Технічно між 4,5-9 годин у вас є точність до 0,98 мілісекунд - я вирішив позначити точку, в якій помилка перевищує 1,00 мілісекунди. Але справа добре взята - точність погіршується на всьому шляху, тому код, який потребує більшої точності, може почати погано поводитися навіть раніше.
DMGregory

16

Максимальне значення поплавця - 3.40282347 * 10 ^ 38, що дорівнює 10 ^ 31 року при вимірюванні в секундах (або 10 ^ 28 років при вимірюванні в мілісекундах). Повірте, це не переповниться.

Єдине, що може з’явитися - це неточність. Але одне точне число з плаваючою комою має точність 7-8 десяткових цифр. Якщо використовувати його для вимірювання секунд, це точно приблизно 194 дні. Якщо вимірювати мілісекунди, то це лише 4,5 години. Отже, це повністю залежить від точності, яка вам потрібна, і вам може знадобитися знайти альтернативні способи, якщо вам потрібно бути точним до мілісекунди (чого, мабуть, немає).


7
Вольфрам Альфа надає тональне уявлення про те, наскільки велика кількість років, це, що, можливо, варто згадати: це приблизно на 20 величин вище, ніж поточна епоха Всесвіту. Не в двадцять разів вище, але поточний вік плюс двадцять більше нулів.
doppelgreener

1
@ Draco18s Залежить від способу вимірювання єдності deltaTime, але якщо вони візьмуть час для кожного кадру та віднять ці два, я вважаю, що відповідь - це точно так.
LukeG

7
Ця відповідь зовсім не правильна. Можна провести експеримент. Ви можете збільшувати плавання по черзі в циклі. Після досягнення 10 000 000 він припиниться збільшуватися. Причина полягає в тому, що ви не можете правильно додавати невеликі числа до великих чисел, коли ви маєте справу з float. Правильну відповідь дав @DMGregory
Чайка

2
@Seagull Ніде не пишу про приріст. Факт, що максимальна кількість, представлене поплавком, становить (приблизно) 3,4 * 10 ^ 38, так що виключає переповнення в суворому розумінні (як це означає в ОП). Я не бачу, чому відповідь була б невірною, як це є?
LukeG

2
@Seagull Я вважаю, що відповідь Люкгена є правильною (навіть до покращення змін) - його відповідь і моя відповідь лише на різні класи потреб. Мені подобається придивлятися до точності поплавця та обмеження випадків, в той час як LukeG дивиться на цю проблему трохи ширше, з меншим акцентом на вимоги до точності.
DMGregory

12

На скільки точності потрібна ваша гра?

32-бітний поплавок має 24 біти точності. Іншими словами, в момент t точність становить ± 2 -23 × t. (Це не зовсім правильно, але це близько, а точні деталі не дуже страшні.)

  • Якщо вам потрібна точність 1 мс, то через 1 мс ÷ 2 -23 = 8400 с = 2 год 20 м 32-бітний поплавок вже не є прийнятним. Точність 1 мс не потрібна для більшості ігор, але вона вважається необхідною для музичних програм.

  • Якщо ваша гра працює на моніторі 144 Гц, і ви хочете графіку з точною рамкою, то після 1 ÷ 144 Гц ÷ 2 -23 = 58000 с = 16 год 32-бітний поплавок вже не є прийнятним. 16 год - це довгий час вести гру, але це немислимо. Б'юсь об заклад, що значний відсоток з нас провів гру протягом 16 годин за один розтяг - навіть якщо тільки тому, що ми залишили гру в бігу і трохи поснули. У цей момент оновлення анімації та фізики було б зменшено до нижче 144 ГГц.

Якщо об'єм Unity Time.deltaTimeобчислюється з тактовою частотою з більш високою точністю, ви можете використовувати це і все-таки отримати повну точність. Я не знаю, правда це чи ні.

Бічна примітка

Ігри та інші програми в Windows часто використовують, GetTickCount()щоб визначити час. Оскільки для підрахунку мілісекунд використовується 32-бітове ціле число, воно обертається приблизно кожні 50 днів, якщо ви залишаєте комп'ютер на такому довгому. Я підозрюю, що багато ігор зависають, розбиваються чи недобре поводяться, якби ви грали в них у 50-денний знак.

Цілі особи, як Windows GetTickCount(), мають ризик переповнення, тоді як плавці, як у Unity Time.time, мають ризик втратити точність.


10

Інші відповіді лише міркують, тому я написав простий проект Unity і дав йому працювати деякий час. Через пару годин це результат:

  • UnityEngine.Time.timeдосить швидко втрачає точність. Як і очікувалося, після запуску гри протягом 4 годин значення підскакують на 1 мілісекунд, а неточність зростає після цього.
  • UnityEngine.Time.deltaTimeзберігає свою початкову субмілісекундну точність навіть після того, як Unity працює протягом кількох годин. Таким чином, практично гарантовано, що deltaTimeце походить від платформового лічильника високої роздільної здатності (який зазвичай переповнюється через 10-1000 років). Залишається крихітний ризик, який deltaTimeвсе одно втратить точність на деяких платформах.

Неточність Часу є проблемою для всього, від чого залежить UnityEngine.Time.time. Одним із конкретних прикладів є обертання (наприклад, вітряк), яке зазвичай ґрунтується UnityEngine.Mathf.Sin(UnityEngine.Time.time*rotationSpeed). Ще більше питання є_Time що явно використовується для анімації шейдерами. Наскільки високою повинна бути неточність, перш ніж вона починає помічати? Точність 20-30 мс (2 дні) повинна стати точкою, коли люди, які знають про проблему та пильну увагу, помітять. Через 50-100 мс (5-10 днів) чутливі люди, які не знають про проблему, почнуть розгадувати її. Через 200-300 мс (20-30 днів) проблема буде очевидною.

До сих пір я не перевіряв точність _SinTime, _CosTimeіUnity_DeltaTime , таким чином , я не можу коментувати це.

Це сценарій, який я використовував для тестування. Він приєднаний до UI.Textелемента, налаштування програвача програму дозволяють запускати проект у фоновому режимі, а VSync у налаштуваннях якості встановлюється на кожне друге V пусте:

public class Time : UnityEngine.MonoBehaviour {
    void Start () {
        LastTime = (double)UnityEngine.Time.time;
        StartTime =  System.DateTime.Now;
        LastPrintTime =  System.DateTime.Now;
    }
    double LastTime;
    System.DateTime StartTime;
    System.DateTime LastPrintTime;
    void Update () {
        double deltaTimeError = (double)UnityEngine.Time.deltaTime - ((double)UnityEngine.Time.time - LastTime);
        if (System.DateTime.Now - LastPrintTime  > System.TimeSpan.FromMilliseconds(1000))
        {
            GetComponent<UnityEngine.UI.Text>().text = "StartTime: " + StartTime.ToLongTimeString()
                + "\nCurrentTime: " + System.DateTime.Now.ToLongTimeString()
                + "\n\nTime.deltaTime: " + UnityEngine.Time.deltaTime.ToString("0.000000")
                + "\nTime.time - LastTime: " + ((float)(UnityEngine.Time.time - LastTime)).ToString("0.000000")
                + "\nAbsolute Error: " +  System.Math.Abs(deltaTimeError).ToString("0.000E0");
            LastPrintTime = System.DateTime.Now;
        }
        LastTime = UnityEngine.Time.time;       
    }
}

введіть тут опис зображеннявведіть тут опис зображеннявведіть тут опис зображення введіть тут опис зображення

Якщо вам цікаво 0.033203значення, я впевнений, що насправді 0.033203125це двійкове представлення з плаваючою комою 00111101000010000000000000000000. Зауважте, що багато людей 0у мантісі.

Обхідні шляхи

Більшість жанрів не потребує вирішення. Більшість гравців навіть не грають у гру за 100 годин, тому вкладати гроші, щоб вирішити проблему, люди помітять лише через кілька днів гіпотетичного безперервного відтворення, економічно не під силу. На жаль, я не можу придумати простий універсальний метод вирішення всієї проблеми, не усуваючи суттєвих недоліків.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.