Time.deltaTime के उपयोग के बावजूद आंदोलन फ्रेम दर पर निर्भर प्रतीत होता है


13

एकता में एक गेम ऑब्जेक्ट को स्थानांतरित करने के लिए आवश्यक अनुवाद की गणना करने के लिए मेरे पास निम्नलिखित कोड है, जिसे में कहा जाता है LateUpdate। मुझे जो समझ में आया है, उसके उपयोग से Time.deltaTimeअंतिम अनुवाद फ्रेम दर को स्वतंत्र करना चाहिए (कृपया ध्यान दें CollisionDetection.Move()कि केवल रेकास्ट प्रदर्शन कर रहा है)।

public IMovementModel Move(IMovementModel model) {    
    this.model = model;

    targetSpeed = (model.HorizontalInput + model.VerticalInput) * model.Speed;

    model.CurrentSpeed = accelerateSpeed(model.CurrentSpeed, targetSpeed,
        model.Accel);

    if (model.IsJumping) {
        model.AmountToMove = new Vector3(model.AmountToMove.x,
            model.AmountToMove.y);
    } else if (CollisionDetection.OnGround) {
        model.AmountToMove = new Vector3(model.AmountToMove.x, 0);
    }

    model.FlipAnim = flipAnimation(targetSpeed);
    // If we're ignoring gravity, then just use the vertical input.
    // if it's 0, then we'll just float.
    gravity = model.IgnoreGravity ? model.VerticalInput : 40f;

    model.AmountToMove = new Vector3(model.CurrentSpeed, model.AmountToMove.y - gravity * Time.deltaTime);

    model.FinalTransform =
        CollisionDetection.Move(model.AmountToMove * Time.deltaTime,
            model.BoxCollider.gameObject, model.IgnorePlayerLayer);
    // Prevent the entity from moving too fast on the y-axis.
    model.FinalTransform = new Vector3(model.FinalTransform.x,
        Mathf.Clamp(model.FinalTransform.y, -1.0f, 1.0f),
        model.FinalTransform.z);

    return model;
}

private float accelerateSpeed(float currSpeed, float target, float accel) {
    if (currSpeed == target) {
        return currSpeed;
    }
    // Must currSpeed be increased or decreased to get closer to target
    float dir = Mathf.Sign(target - currSpeed);
    currSpeed += accel * Time.deltaTime * dir;
    // If currSpeed has now passed Target then return Target, otherwise return currSpeed
    return (dir == Mathf.Sign(target - currSpeed)) ? currSpeed : target;
}

private void OnMovementCalculated(IMovementModel model) {
    transform.Translate(model.FinalTransform);
}

अगर मैं गेम के फ़्रैमरेट को 60FPS पर लॉक कर दूं, तो मेरी ऑब्जेक्ट्स अपेक्षा के अनुरूप चलती हैं। हालांकि, अगर मैं इसे अनलॉक करता हूं ( Application.targetFrameRate = -1;), कुछ ऑब्जेक्ट बहुत धीमी दर से आगे बढ़ेंगे तो मैं उम्मीद करूंगा कि 144hz मॉनिटर पर ~ 200FPS प्राप्त हो। यह केवल एक स्वसंपूर्ण निर्माण में लगता है, और एकता संपादक के भीतर नहीं।

संपादक के भीतर वस्तु आंदोलन का जीआईएफ, एफपीएस खुला

http://gfycat.com/SmugAnnualFugu

स्टैंडअलोन बिल्ड के भीतर वस्तु आंदोलन का GIF, खुला हुआ एफपीएस

http://gfycat.com/OldAmpleJuliabutterfly


2
आपको इसे पढ़ना चाहिए। समय बाल्टी क्या आप चाहते हैं, और निश्चित समय कदम है! gafferongames.com/game-physics/fix-your-timestep
एलन वोल्फ

जवाबों:


30

फ़्रेम आधारित सिमुलेशन त्रुटियों का अनुभव करेंगे जब अद्यतन परिवर्तन की गैर-रैखिक दरों की भरपाई करने में विफल हो जाते हैं।

उदाहरण के लिए शून्य की स्थिति और वेग मूल्यों के साथ शुरू होने वाली एक वस्तु पर विचार करें जो एक के निरंतर त्वरण का अनुभव कर रहा है।

यदि हम यह अद्यतन तर्क लागू करते हैं:

velocity += acceleration * elapsedTime
position += velocity * elapsedTime

हम अलग-अलग फ्रेम दरों के तहत इन परिणामों की उम्मीद कर सकते हैं: यहाँ छवि विवरण दर्ज करें

त्रुटि अंतिम वेग का इलाज करने के कारण होती है क्योंकि यह पूरे फ्रेम के लिए लागू होती है। यह एक राइट रिमन सम के समान है और त्रुटि की मात्रा फ्रेम दर (एक अलग फ़ंक्शन पर सचित्र) के साथ भिन्न होती है:

जैसा कि माइकलएस बताते हैं, फ़्रेम की अवधि आधी होने पर यह त्रुटि आधी हो जाएगी और उच्च फ्रेम दर पर असंगत हो सकती है। दूसरी ओर, कोई भी खेल जो प्रदर्शन स्पाइक्स या लंबे समय तक चलने वाले तख्ते का अनुभव करता है, यह अप्रत्याशित व्यवहार पैदा कर सकता है।


सौभाग्य से किनेमैटिक्स हमें रैखिक त्वरण के कारण विस्थापन की सही गणना करने की अनुमति देता है:

d =  vᵢ*t + (a*t²)/2

where:
  d  = displacement
  v = initial velocity
  a  = acceleration
  t  = elapsed time

breakdown:
  vᵢ*t     = movement due to the initial velocity
  (a*t²)/2 = change in movement due to acceleration throughout the frame

इसलिए यदि हम इस अद्यतन तर्क को लागू करते हैं:

position += (velocity * elapsedTime) + (acceleration * elapsedTime * elapsedTime / 2)
velocity += acceleration * elapsedTime

हम निम्नलिखित परिणाम होगा:

यहाँ छवि विवरण दर्ज करें


2
यह उपयोगी जानकारी है, लेकिन यह वास्तव में कोड को कैसे संबोधित करता है? सबसे पहले, फ्रैमर्ट बढ़ने पर त्रुटि नाटकीय रूप से कम हो जाती है, इसलिए 60 और 200 एफपीएस के बीच का अंतर नगण्य है (8 एफपीएस बनाम अनंत पहले से ही केवल 12.5% ​​बहुत अधिक है)। दूसरा, एक बार स्प्राइट पूरी गति से होने के बाद, सबसे बड़ा अंतर 0.5 इकाइयों से आगे हो रहा है। यह वास्तविक चलने की गति को प्रभावित नहीं करना चाहिए जैसा कि .gif से जुड़ा हुआ है। जब वे चारों ओर मुड़ते हैं, तो त्वरण तुरंत प्रतीत होता है (संभवतः 60+ एफपीएस पर कई फ्रेम, लेकिन पूर्ण सेकंड नहीं)।
माइकल

2
यह एक एकता या कोड का मुद्दा है, फिर गणित का मुद्दा नहीं। एक त्वरित स्प्रेडशीट कहती है कि यदि हम a = 1, vi = 0, di = 0, vmax = 1 का उपयोग करते हैं, तो हमें d = 0.5 के साथ t = 1 पर vmax को हिट करना चाहिए। ऐसा करते हुए 5 फ्रेम (dt = 0.2), d (t = 1) = 0.6। 50 फ्रेम (dt = 0.02), d (t = 1) = 0.51। 500 से अधिक फ्रेम (dt = 0.002), d (t = 1) = 0.501। तो 5 एफपीएस 20% ऊंचा है, 50 एफपीएस 2% अधिक है, और 500 एफपीएस 0.2% उच्च है। सामान्य तौर पर, त्रुटि 100 / एफपीएस प्रतिशत बहुत अधिक है। 50 एफपीएस 500 एफपीएस से लगभग 1.8% अधिक है। और यह सिर्फ त्वरण के दौरान है। एक बार जब वेग अधिकतम हो जाता है, तो शून्य अंतर होना चाहिए। एक = 100 और vmax = 5 के साथ, कम अंतर भी होना चाहिए।
माइकल

2
वास्तव में, मैंने आगे बढ़कर एक VB.net ऐप में आपके कोड का उपयोग किया (1/60 और 1/200 का सिमुलेशन dt), और बाउंस मिला : 5 फ्रेम 626 (10.433) सेकंड बनाम बाउंस: 5 एट फ्रेम 2081 ( 10.405) सेकंड । 60 एफपीएस पर 0.27% अधिक समय।
माइकल एफएस

2
यह आपका "कीनेमेटिक" दृष्टिकोण है जो 10% अंतर देता है। पारंपरिक दृष्टिकोण में 0.27% का अंतर है। आपने उन्हें गलत तरीके से लेबल किया है। मुझे लगता है कि क्योंकि वेग गलत होने पर आप त्वरण सहित गलत तरीके से शामिल होते हैं। उच्च फ्रैमरेट्स प्रति फ्रेम कम त्रुटि जोड़ते हैं, इसलिए अधिक सटीक परिणाम देते हैं। आपको जरूरत है if(velocity==vmax||velocity==-vmax){acceleration=0}। फिर त्रुटि काफी हद तक कम हो जाती है, हालांकि यह सही नहीं है क्योंकि हम यह पता नहीं लगाते हैं कि फ्रेम त्वरण का कौन सा हिस्सा समाप्त हो गया है।
माइकल

6

यह इस बात पर निर्भर करता है कि आप अपना कदम कहां से बुला रहे हैं। यदि आप इसे अपडेट से बुला रहे हैं, तो आपका आंदोलन वास्तव में स्वतंत्र हो जाएगा यदि आप Time.deltaTime के साथ स्केल करते हैं, लेकिन यदि आप इसे FixedUpdate से कॉल कर रहे हैं, तो आपको Time.fixedDeltaTime के साथ स्केल करने की आवश्यकता है। मुझे लगता है कि आप अपने कदम को FixedUpdate से बुला रहे हैं, लेकिन Time.deltaTime के साथ स्केलिंग, जिसके परिणामस्वरूप यूनिटी का निर्धारित चरण मुख्य लूप की तुलना में धीमा होने पर स्पष्ट गति में कमी आएगी, जो कि आपके स्टैंडअलोन बिल्ड में हो रहा है। जब फिक्स्ड स्टेप धीमा होता है, तो फिक्स्डलाइट टाइम बड़ा होता है।


1
इसे लेटउपडेट से बुलाया जा रहा है। मैं अपने प्रश्न को स्पष्ट करने के लिए अपडेट करूंगा। हालांकि मेरा मानना ​​है कि Time.deltaTimeयह अभी भी सही मूल्य का उपयोग करेगा जहां इसे कहा जाता है (यदि फिक्स्डयूडेट में उपयोग किया जाता है, तो यह फिक्स्डप्लेटटाइम का उपयोग करेगा)।
कूपर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.