Apa yang terjadi ketika Time.time menjadi sangat besar di Unity?

37

Seperti yang dikatakan di sini :

Waktu

Waktu di awal bingkai ini (Hanya Baca). Ini adalah waktu dalam hitungan detik sejak awal permainan.

Dan seperti yang saya tahu waktu disimpan di float. Jadi, pertanyaan saya adalah apa yang akan terjadi ketika nilai waktu menjadi sangat besar? Bisakah itu meluap? Apakah akan kehilangan presisi dan menyebabkan bug? Apakah gamenya hanya crash atau apa?

Yaroslav
sumber
6
Hanya untuk konteks, lihat Guinness World Record pada maraton videogame terpanjang .
Theraot
5
@AlexandreVaillancourt Saya pikir ada pertanyaan yang berguna di sini jika kita menafsirkannya sebagai "Apakah ada masalah ketika Time.time menjadi sangat besar?" sebagai lawan fokus secara harfiah pada "meluap" sendiri. Saya telah mengedit pertanyaan di sepanjang baris ini untuk mencoba menjawab umpan balik Anda.
DMGregory
2
@ DMGregory Tapi pertanyaannya sudah dijawab dan diterima ... Saya setuju hasil edit Anda untuk pertanyaan yang lebih berguna, hanya sedikit terlambat.
MichaelHouse
Saya mencoba untuk frase edit sehingga jawaban yang diterima masih merupakan jawaban yang benar untuk versi yang diedit. (Lagi pula, itu sudah berbicara tentang masalah presisi) Saya tidak akan tersinggung jika itu dibatalkan.
DMGregory

Jawaban:

62

Ada risiko nyata hilangnya ketepatan waktu dari penggunaan pelampung presisi tunggal .

Itu masih akan mempertahankan akurasi hingga detik terdekat selama 97 hari . Tetapi dalam game kita sering peduli tentang akurasi pada urutan durasi frame.

Nilai waktu float presisi tunggal dalam detik mulai kehilangan akurasi milidetik setelah sekitar 9 jam .

Itu sudah tidak di luar bidang kemungkinan untuk sesi bermain tunggal, atau membiarkan permainan berjalan "AFK" saat Anda sedang bekerja / sekolah atau tidur. (Ini adalah salah satu alasan mengapa cara umum untuk menguji permainan adalah menjalankannya dalam semalam dan memeriksa apakah ia masih bermain dengan benar di pagi hari)

Pada konsol modern, di mana game sering ditunda dan dilanjutkan di antara sesi bermain daripada ditutup sepenuhnya, itu tidak terduga untuk "sesi tunggal" dari pandangan mata game untuk melebihi 9 jam runtime bahkan ketika dimainkan dalam ledakan singkat.

Dengan asumsi Unity's deltaTimedihitung dari sumber waktu dengan presisi lebih tinggi, yang dibuktikan oleh eksperimen Peter dalam jawaban lain, mengandalkan deltaTimeuntuk penentuan skala waktu relatif aman (hanya berhati-hati dalam mengumpulkan durasi yang sangat lama dengan menjumlahkan deltaTimenilai - menambahkan sedikit peningkatan pada float yang lebih besar adalah resep klasik untuk kehilangan presisi, bahkan ketika Anda mengimbangi dengan algoritma yang cerdas ).

Karena fixedDeltaTimemempertahankan nilai yang sama dengan yang Anda tetapkan, alih-alih berubah secara dinamis dari satu frame ke frame, Anda juga dapat menempatkan perilaku sensitif waktu dalam FixedUpdate untuk mendapatkan jaminan konsistensi yang lebih kuat. deltaTimeakan secara otomatis mengembalikan delta tetap yang sesuai dalam metode ini. Meskipun ada frekuensi denyut antara pembaruan bingkai & tetap, Anda dapat memuluskan ini melalui interpolasi .

Yang ingin Anda hindari adalah menghitung durasi dengan mengurangi satu stempel waktu dari yang lain . Setelah berjam-jam bermain, ini dapat menyebabkan pembatalan bencana, yang menyebabkan ketepatan yang jauh lebih rendah daripada yang Anda dapatkan di awal pertandingan. Jika membandingkan cap waktu penting untuk sistem Anda, Anda dapat menghasilkan nilai waktu resolusi lebih tinggi dengan menggunakan metode lain, seperti System.Diagnostics.Stopwatchsebagai ganti Time.time.

DMGregory
sumber
Unreal juga memilih untuk mengekspos waktu game sebagai float presisi tunggal, baik dalam kode dan cetak biru . Anda dapat mengatasinya dengan cap waktu secara realtime dan melakukan pelebaran waktu Anda sendiri, sama seperti yang Anda lakukan di Unity. (Hanya perhatikan bahwa jenis cap waktu Unreal didasarkan pada zaman 32-bit ). Ekstensi memang ada untuk C # di Unreal jika Anda memilihnya.
DMGregory
Untuk [ms], Anda mulai kehilangan presisi sekitar 16 484 - 32 768 band. Artinya, Anda mungkin kehilangan presisi setelah 4 jam 30 menit , setelah 9 jam Anda pasti tidak bisa mempercayainya.
xmedeko
Secara teknis antara 4,5-9 jam, Anda memiliki ketepatan hingga dalam 0,98 milidetik - Saya memilih untuk menandai titik di mana kesalahan melebihi 1,00 milidetik. Tapi intinya diambil dengan baik - presisi menurun sepanjang jalan, jadi kode yang membutuhkan lebih presisi mungkin mulai bertingkah buruk bahkan lebih awal.
DMGregory
16

Nilai maksimum float adalah 3,40282347 * 10 ^ 38, yang sama dengan 10 ^ 31 tahun bila diukur dalam detik (atau 10 ^ 28 tahun bila diukur dalam milidetik). Percayalah, itu tidak akan meluap.

Satu-satunya hal yang mungkin muncul adalah ketidaktepatan. Tetapi satu angka floating point presisi memiliki akurasi 7-8 angka desimal. Jika menggunakannya untuk mengukur detik, itu akurat selama sekitar 194 hari. Jika mengukur milidetik hanya akurat 4,5 jam. Jadi itu benar-benar tergantung pada keakuratan yang Anda butuhkan dan Anda mungkin perlu menemukan cara alternatif jika Anda harus akurat ke milidetik (yang mungkin tidak Anda lakukan).

LukeG
sumber
7
Wolfram Alpha memberikan gagasan pengaturan nada tentang seberapa besar beberapa tahun yang mungkin layak disebutkan: itu kira-kira 20 magnitude lebih tinggi dari usia alam semesta saat ini. Bukan dua puluh kali lebih tinggi, tetapi usia saat ini ditambah dua puluh nol lagi.
doppelgreener
1
@ Draco18s Tergantung pada cara kesatuan mengukur deltaTime, tetapi jika mereka mengambil titik waktu untuk setiap frame dan mengurangi keduanya, saya kira jawabannya pasti ya.
LukeG
7
Jawaban ini sama sekali tidak benar. Anda dapat menjalankan percobaan. Anda dapat menambah float satu per satu di loop. Setelah mencapai 10.000, itu akan berhenti bertambah. Alasannya adalah Anda tidak dapat menambahkan angka kecil ke angka besar dengan benar ketika Anda berurusan dengan float. Jawaban yang tepat diberikan oleh @DMGregory
Seagull
2
@ Seagull Tidak ada tempat saya menulis tentang penambahan. Itu fakta bahwa jumlah maksimum yang diwakili oleh pelampung adalah (sekitar) 3,4 * 10 ^ 38, sehingga aturan keluar meluap dalam arti yang ketat (sebagaimana dimaksud oleh OP). Saya tidak melihat mengapa jawabannya salah seperti halnya tegakan?
LukeG
2
@ Seagull Saya kira jawaban LukeG benar (bahkan sebelum perbaikan dalam penyuntingan) - jawabannya dan milik saya hanya merujuk pada kelas kebutuhan yang berbeda. Saya suka mencari tahu tentang presisi float dan membatasi kasus, sementara LukeG melihat masalah ini sedikit lebih luas, dengan sedikit penekanan pada kebutuhan presisi presisi.
DMGregory
12

Berapa presisi yang dibutuhkan gim Anda?

Pelampung 32-bit memiliki presisi 24 bit. Dengan kata lain, pada waktu t, presisi adalah ± 2 -23 × t. (Ini tidak sepenuhnya benar, tetapi dekat, dan detail yang tepat tidak terlalu menarik.)

  • Jika Anda membutuhkan ketelitian 1ms, maka setelah 1ms ÷ 2 -23 = 8400 s = 2j 20m, pelampung 32-bit tidak lagi dapat diterima. Ketelitian 1ms tidak diperlukan untuk sebagian besar gim, tetapi dianggap perlu untuk aplikasi musik.

  • Jika game Anda berjalan pada monitor 144 Hz, dan Anda menginginkan grafik dengan frame-akurat, maka setelah 1 ÷ 144 Hz ÷ 2 -23 = 58000 s = 16j, pelampung 32-bit tidak lagi dapat diterima. 16h adalah waktu yang lama untuk menjalankan permainan, tetapi itu tidak terbayangkan. Saya yakin sebagian besar dari kita telah menjalankan permainan selama 16 jam dalam satu rentang - bahkan jika hanya karena kita membiarkan permainan berjalan dan tidur. Pada saat itu, pembaruan animasi dan fisika akan dikurangi hingga di bawah 144Hz.

Jika Unity's Time.deltaTimedihitung dari jam dengan presisi lebih tinggi, maka Anda bisa menggunakannya dan masih mendapatkan presisi penuh. Saya tidak tahu apakah ini benar atau tidak.

Catatan Samping

Game dan program lain di Windows sering digunakan GetTickCount()untuk mengetahui waktu. Karena menggunakan bilangan bulat 32-bit untuk menghitung milidetik, ia membungkus sekitar setiap 50 hari, jika Anda meninggalkan komputer Anda selama itu. Saya menduga bahwa banyak permainan akan hang, crash, atau bertingkah aneh dengan cara yang aneh jika Anda memainkannya pada tanda 50 hari.

Integer, seperti Windows ' GetTickCount(), memiliki risiko overflow, sedangkan float, seperti Unity Time.time, memiliki risiko kehilangan presisi.

Dietrich Epp
sumber
10

Jawaban lainnya hanya berspekulasi, jadi saya menulis proyek Unity sederhana dan membiarkannya berjalan untuk sementara waktu. Setelah beberapa jam, inilah hasilnya:

  • UnityEngine.Time.timekehilangan akurasi agak cepat. Seperti yang diharapkan, setelah menjalankan game selama 4 jam nilainya melonjak 1 milidetik, dan ketidaktepatan meningkat setelah itu.
  • UnityEngine.Time.deltaTimemenjaga akurasi awal sub-milidetik bahkan setelah Unity berjalan selama berjam-jam. Dengan demikian, dijamin deltaTimedari turunan resolusi tinggi yang tergantung platform (yang biasanya meluap setelah 10-1000 tahun). Masih ada risiko kecil yang deltaTimemasih akan kehilangan presisi pada beberapa platform.

Ketidaktepatan Waktu adalah masalah untuk semua yang bergantung pada UnityEngine.Time.time. Salah satu contoh spesifik adalah rotasi (misalnya kincir angin), yang biasanya didasarkan pada UnityEngine.Mathf.Sin(UnityEngine.Time.time*rotationSpeed). Bahkan lebih banyak masalah _Timeyang secara eksplisit digunakan untuk animasi oleh shader. Seberapa tinggi ketidaktelitian yang dibutuhkan sebelum mulai terlihat? Akurasi 20-30 ms (2 hari) harus menjadi titik di mana orang yang menyadari masalah ini, dan memperhatikan, akan melihat. Pada 50-100 ms (5-10 hari) orang-orang sensitif yang tidak tahu tentang masalah akan mulai mencari tahu. Dari 200-300 ms (20-30 hari) masalahnya akan jelas.

Sejauh ini saya belum diuji akurasi _SinTime, _CosTimedan Unity_DeltaTime, jadi saya tidak bisa mengomentari ini.

Ini adalah Skrip yang saya gunakan untuk pengujian. Itu dilampirkan ke UI.Textelemen, Pengaturan Pemain dari proyek memungkinkan proyek untuk menjalankan sementara di latar belakang, dan VSync dalam Pengaturan Kualitas diatur ke Setiap Detik V Kosong:

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

masukkan deskripsi gambar di sinimasukkan deskripsi gambar di sinimasukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Jika Anda bertanya-tanya tentang 0.033203nilai, saya cukup yakin itu sebenarnya 0.033203125, yang memiliki representasi titik mengambang biner 00111101000010000000000000000000. Perhatikan banyak 0di mantissa.

Penanganan masalah

Sebagian besar genre tidak membutuhkan solusi. Sebagian besar pemain bahkan tidak akan memainkan permainan untuk total 100 jam, jadi menginvestasikan uang untuk memperbaiki masalah yang orang hanya akan perhatikan setelah beberapa hari bermain secara terus-menerus secara hipotesis tidak layak secara ekonomi. Sayangnya saya tidak dapat menemukan solusi universal sederhana yang memperbaiki keseluruhan masalah tanpa membawa beberapa kelemahan yang signifikan.

Peter
sumber