จะเกิดอะไรขึ้นเมื่อ Time.time มีขนาดใหญ่มากใน Unity?


37

ตามที่กล่าวไว้ที่นี่ :

Time.time

เวลาที่จุดเริ่มต้นของเฟรมนี้ (อ่านอย่างเดียว) นี่คือเวลาในไม่กี่วินาทีนับตั้งแต่เริ่มเกม

และอย่างที่ฉันรู้ว่าเวลาถูกเก็บไว้ในโฟลต ดังนั้นคำถามของฉันคือสิ่งที่จะเกิดขึ้นเมื่อมูลค่าของเวลามีขนาดใหญ่มาก? มันล้นได้ไหม มันจะสูญเสียความแม่นยำและทำให้เกิดข้อบกพร่องหรือไม่ เกมจะมีปัญหาหรือไม่


6
เพียงแค่ให้บริบทดูที่กินเนสส์เวิลด์เร็กคอร์ดบนที่ยาวที่สุดมาราธอนวีดีโอ
ธีระโรจน์

5
@AlexandreVaillancourt ฉันคิดว่ามีคำถามที่เป็นประโยชน์ที่นี่ถ้าเราตีความว่าเป็น "มีปัญหาเมื่อ Time.time ใหญ่ขึ้นหรือไม่" ซึ่งตรงข้ามกับการมุ่งเน้นที่ "ล้น" เพียงอย่างเดียว ฉันได้แก้ไขคำถามในบรรทัดเหล่านี้เพื่อพยายามตอบข้อเสนอแนะของคุณ
DMGregory

2
@DMGregory แต่คำถามได้รับคำตอบและได้รับการยอมรับแล้ว ... ฉันเห็นด้วยกับการแก้ไขของคุณสำหรับคำถามที่มีประโยชน์มากกว่านี้
MichaelHouse

ฉันพยายามใช้วลีแก้ไขเพื่อให้คำตอบที่ยอมรับยังคงเป็นคำตอบที่ถูกต้องสำหรับรุ่นที่แก้ไข (ท้ายที่สุดแล้วมันก็พูดถึงปัญหาที่มีความแม่นยำ) ฉันจะไม่ผิดถ้าย้อนกลับไป
DMGregory

คำตอบ:


62

มีความเสี่ยงที่แท้จริงต่อการสูญเสียความแม่นยำของเวลาจากการใช้โฟลทที่มีความแม่นยำเดียว

มันจะยังคงรักษาความถูกต้องเพื่อที่สองที่ใกล้ที่สุดสำหรับ 97 วัน แต่ในเกมเรามักจะสนใจเกี่ยวกับความถูกต้องตามลำดับระยะเวลาของเฟรม

เดียวแม่นยำค่าเวลาลอยในไม่กี่วินาทีเริ่มที่จะสูญเสียความถูกต้องมิลลิวินาทีหลังจากนั้นประมาณ9 ชั่วโมง

นั่นไม่ได้อยู่นอกขอบเขตของความเป็นไปได้สำหรับเซสชั่นการเล่นเดียวหรือออกจากเกมที่ใช้ "AFK" ในขณะที่คุณกำลังทำงาน / โรงเรียนหรือนอนหลับ (นี่คือเหตุผลหนึ่งว่าทำไมวิธีทั่วไปในการทดสอบเกมคือเรียกใช้ข้ามคืนและตรวจสอบว่าเกมยังคงเล่นอย่างถูกต้องในตอนเช้า)

บนคอนโซลที่ทันสมัยซึ่งเกมมักถูกหยุดชั่วคราวและเล่นต่อระหว่างเซสชันการเล่นแทนที่จะปิดตัวลงอย่างสมบูรณ์มันไม่ได้คาดหวังสำหรับ "เซสชั่นเดียว" จากมุมมองตาของเกมนานกว่า 9 ชั่วโมงของรันไทม์แม้ว่าจะเล่นในระยะสั้น

สมมติว่าเอกภาพของdeltaTimeนวณจากแหล่งเวลาที่สูงขึ้นมีความแม่นยำซึ่งเป็นลมออกจากการทดลองปีเตอร์ในคำตอบอื่นอาศัยdeltaTimeสำหรับการกำหนดเวลากรอบขนาดค่อนข้างปลอดภัย (เพียงระมัดระวังของสะสมระยะเวลานานมากจากข้อสรุปdeltaTimeค่า - เพิ่มทีละน้อยไป float ที่ใหญ่ขึ้นเป็นสูตรดั้งเดิมสำหรับการสูญเสียความแม่นยำแม้เมื่อคุณชดเชยด้วยอัลกอริทึมที่เข้าใจ )

เนื่องจากfixedDeltaTimeรักษาค่าเดิมที่คุณตั้งไว้แทนที่จะเปลี่ยนจากเฟรมเป็นเฟรมแบบไดนามิกคุณยังสามารถใส่ลักษณะการตอบสนองตามกำหนดเวลาใน FixUpdate เพื่อรับการรับประกันที่มั่นคงยิ่งขึ้น deltaTimeจะส่งคืนเดลต้าถาวรที่เหมาะสมโดยอัตโนมัติในวิธีการเหล่านี้ ในขณะที่อาจมีความถี่จังหวะระหว่างการปรับปรุงและการแก้ไขกรอบคุณสามารถเรียบออกนี้ผ่านการแก้ไข

สิ่งที่คุณต้องการที่จะหลีกเลี่ยงการอยู่ในการคำนวณระยะเวลาโดยการลบหนึ่งประทับเวลาจากที่อื่น หลังจากเล่นไปหลายชั่วโมงสิ่งนี้อาจทำให้เกิดการยกเลิกหายนะซึ่งนำไปสู่ความแม่นยำน้อยกว่าที่คุณได้รับในระยะแรก หากเปรียบเทียบการประทับเวลาเป็นสิ่งที่สำคัญกับระบบของคุณคุณสามารถสร้างค่าเวลาที่มีความละเอียดสูงของคุณเองโดยใช้วิธีการอื่น ๆ เช่นการแทนSystem.Diagnostics.StopwatchTime.time


Unreal ยังเลือกที่จะเปิดเผยเวลาเล่นเกมเป็นลอยแม่นยำเดียวทั้งในรหัสและพิมพ์เขียว คุณสามารถแก้ไขได้ด้วยการประทับเวลาแบบเรียลไทม์และทำการขยายเวลาของคุณเองเช่นเดียวกับที่คุณทำใน Unity (เพียงระวังประเภทการประทับเวลาของ Unreal ขึ้นอยู่กับยุค32 บิต ) ส่วนขยายมีอยู่สำหรับ C # ในไม่จริงหากคุณเลือก
DMGregory

สำหรับ [ms] คุณจะเริ่มลดความแม่นยำประมาณ 16 484 - 32 768 แบนด์ นั่นคือคุณอาจสูญเสียความแม่นยำหลังจาก4h30 นาทีหลังจาก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
Wolfram Alpha นำเสนอแนวคิดการตั้งค่าเสียงของจำนวนปีที่อาจมีค่ามากที่กล่าวถึง: ที่สูงกว่าประมาณ 20 เท่าของอายุปัจจุบันของจักรวาล ไม่สูงกว่ายี่สิบเท่าแต่อายุปัจจุบันบวกศูนย์อีกยี่สิบ
doppelgreener

1
@ Draco18s ขึ้นอยู่กับความเป็นเอกภาพของการวัดเดลตาไทม์ แต่ถ้าพวกเขาใช้จุดเวลาสำหรับแต่ละเฟรมและลบทั้งสองนั้นฉันคิดว่าคำตอบนั้นแน่นอนใช่
LukeG

7
คำตอบนี้ไม่ถูกต้องเลย คุณสามารถเรียกใช้การทดสอบ คุณอาจเพิ่มขึ้นทีละหนึ่งในวง หลังจากถึง 10,000,000 มันจะหยุดเพิ่มขึ้น เหตุผลคือคุณไม่สามารถเพิ่มตัวเลขขนาดเล็กเป็นจำนวนมากได้อย่างถูกต้องเมื่อคุณจัดการกับลอย คำตอบที่ถูกต้องได้รับจาก @DMGregory
Seagull

2
@Seagull ไม่มีที่ไหนที่ฉันจะเขียนเกี่ยวกับการเพิ่มขึ้น มันเป็นความจริงที่ว่าจำนวนสูงสุดที่สามารถแสดงได้โดยโฟลตคือ (ประมาณ) 3,4 * 10 ^ 38 ดังนั้นกฎนี้จึงออกมาอย่างล้นหลามในความหมายที่เข้มงวด (ตามที่ OP หมายถึง) ฉันไม่เห็นว่าทำไมคำตอบจะไม่ถูกต้องตามที่เป็นอยู่หรือไม่
LukeG

2
@Seagull ฉันคิดว่าคำตอบของ LukeG นั้นถูกต้อง (ก่อนที่จะมีการปรับปรุงแก้ไข) - คำตอบของเขาและฉันก็อ้างถึงความต้องการที่แตกต่างกัน ฉันชอบที่จะพูดถึงความแม่นยำในการลอยตัวและการ จำกัด เคสในขณะที่ลุคของเรามองปัญหาในวงกว้างขึ้นเล็กน้อยโดยเน้นที่ความต้องการความแม่นยำที่แม่นยำน้อยกว่า
DMGregory

12

เกมของคุณต้องการความแม่นยำเท่าใด

ทุ่น 32 บิตมีความแม่นยำ 24 บิต กล่าวอีกนัยหนึ่งในเวลา t ความแม่นยำคือ± 2 -23 × t (สิ่งนี้ไม่ถูกต้อง แต่ใกล้และรายละเอียดที่แน่นอนไม่น่าสนใจอย่างยิ่ง)

  • หากคุณต้องการความแม่นยำ 1 มิลลิวินาทีจากนั้นหลังจาก 1 มิลลิวินาที-2 -23 = 8400 s = 2h 20m การลอยแบบ 32 บิตจะไม่เป็นที่ยอมรับอีกต่อไป ความแม่นยำ 1ms ไม่จำเป็นสำหรับเกมส่วนใหญ่ แต่ก็ถือว่าจำเป็นสำหรับแอพพลิเคชั่นทางดนตรี

  • หากเกมของคุณทำงานบนหน้าจอ 144 Hz และคุณต้องการกราฟิกที่มีความแม่นยำของเฟรมจากนั้นหลังจาก 1 ÷ 144 Hz ÷ 2 -23 = 58000 s = 16h, การลอยแบบ 32 บิตจะไม่เป็นที่ยอมรับอีกต่อไป 16h เป็นเวลานานในการรันเกม แต่มันก็ไม่น่าเชื่อ ฉันเดิมพันเปอร์เซ็นต์ที่สำคัญของเราได้เล่นเกมเป็นเวลา 16 ชั่วโมงในการยืดเดียว - แม้ว่าเพียงเพราะเราออกจากเกมวิ่งและนอนหลับ ณ จุดนั้นภาพเคลื่อนไหวและการอัพเดททางฟิสิกส์จะลดลงเหลือต่ำกว่า 144Hz

หาก Unity's Time.deltaTimeคำนวณจากนาฬิกาที่มีความแม่นยำสูงกว่าคุณสามารถใช้และยังคงได้ความแม่นยำเต็มที่ ฉันไม่รู้ว่านี่เป็นเรื่องจริงหรือไม่

หมายเหตุด้านข้าง

เกมและโปรแกรมอื่น ๆ บน Windows มักใช้GetTickCount()เพื่อหาเวลา เนื่องจากมันใช้เลขจำนวนเต็ม 32 บิตในการนับมิลลิวินาทีมันจะวนรอบทุก ๆ 50 วันถ้าคุณปล่อยให้คอมพิวเตอร์ของคุณอยู่ในช่วงเวลานั้น ฉันสงสัยว่าเกมหลายเกมจะวางตัวผิดพลาดหรือประพฤติตัวไม่เหมาะสมหากคุณเล่นเกมที่เครื่องหมาย 50 วัน

จำนวนเต็มเช่น Windows ' GetTickCount()มีความเสี่ยงของการล้นในขณะที่ลอยเช่น Unity's 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สิ่งที่ใช้อย่างชัดเจนสำหรับภาพเคลื่อนไหวโดย shaders ความไม่ถูกต้องจะต้องสูงขนาดไหนก่อนที่จะเริ่มสังเกตเห็น? ความถูกต้อง 20-30 ms (2 วัน) ควรเป็นจุดที่ผู้ที่ตระหนักถึงปัญหาและใส่ใจอย่างใกล้ชิดจะสังเกตเห็น ที่คนละเอียดอ่อนที่ 50-100 มิลลิวินาที (5-10 วัน) ที่ไม่รู้เกี่ยวกับปัญหาจะเริ่มคิดออก จาก 200-300 ms (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สังเกตหลาย0s ใน mantissa

วิธีการแก้ปัญหา

ประเภทส่วนใหญ่ไม่จำเป็นต้องมีวิธีแก้ไข ผู้เล่นส่วนใหญ่จะไม่ได้เล่นเกมเป็นเวลา 100 ชั่วโมงดังนั้นการลงทุนเพื่อแก้ไขปัญหาที่ผู้คนจะสังเกตเห็นหลังจากการเล่นอย่างต่อเนื่องไม่กี่วันนั้นไม่คุ้มค่า น่าเสียดายที่ฉันไม่สามารถหาวิธีแก้ปัญหาทั่วไปที่เรียบง่ายซึ่งแก้ไขปัญหาทั้งหมดได้โดยไม่ต้องดำเนินการตามข้อบกพร่องที่สำคัญ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.