बेजियर कर्व पर आंदोलन की समान गति कैसे प्राप्त करें?


22

मैं बेज़ियर वक्र के साथ एक छवि को स्थानांतरित करने की कोशिश कर रहा हूं। यह मेरा इसे करने का तरीका है:

- (void)startFly
{    
 [self runAction:[CCSequence actions:
             [CCBezierBy actionWithDuration:timeFlying bezier:[self getPathWithDirection:currentDirection]],
             [CCCallFuncN actionWithTarget:self selector:@selector(endFly)],
             nil]];

}

मेरा मुद्दा यह है कि छवि समान रूप से नहीं चलती है। शुरुआत में यह धीरे-धीरे आगे बढ़ रहा है और फिर धीरे-धीरे तेज हो रहा है और अंत में यह बहुत तेजी से आगे बढ़ रहा है। इस त्वरण से छुटकारा पाने के लिए मुझे क्या करना चाहिए?

जवाबों:


27

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

इस धारणा को बनाने से, दो वैक्टर (क्यूबिक बेजियर कर्व्स, आदि के लिए तीन) से अधिक कुछ भी पूर्व निर्धारित करने की आवश्यकता नहीं है ।

तो एक वक्र हम बिंदु पर इसके स्पर्शरेखा वेक्टर गणना करते हैं । इस वेक्टर का मानदंड और इस प्रकार एक अवधि लिए यात्रा की गई दूरी को रूप में अनुमानित किया जा सकता । यह इस प्रकार है कि एक दूरी एक अवधि ।M(t)dMdttdMdTΔtdMdTΔtLL÷dMdT

आवेदन: द्विघात बेज़ियर वक्र

यदि बेज़ियर वक्र के नियंत्रण बिंदु , और , तो प्रक्षेपवक्र को इस प्रकार व्यक्त किया जा सकता है:ABC

M(t)=(1t)2A+2t(1t)B+t2C=t2(A2B+C)+t(2A+2B)+A

तो व्युत्पन्न है:

dMdt=t(2A4B+2C)+(2A+2B)

आपको बस कहीं पर vectors और संग्रहीत करने की आवश्यकता है। फिर, दिए गए , यदि आप लम्बाई को आगे बढ़ाना चाहते हैं, तो आप निम्न कार्य करते हैं:v1=2A4B+2Cv2=2A+2BtL

t=t+Llength(tv1+v2)

घन बेज़ियर घटता है

एक ही तर्क चार नियंत्रण बिंदुओं , , और साथ एक वक्र पर लागू होता है :ABCD

M(t)=(1t)3A+3t(1t)2B+3t2(1t)C+t3D=t3(A+3B3C+D)+t2(3A6B+3C)+t(3A+3B)+A

व्युत्पन्न है:

dMdt=t2(3A+9B9C+3D)+t(6A12B+6C)+(3A+3B)

हम तीन वैक्टर को रोकते हैं:

v1=3A+9B9C+3Dv2=6A12B+6Cv3=3A+3B

और अंतिम सूत्र है:

t=t+Llength(t2v1+tv2+v3)

सटीकता मुद्दों

यदि आप एक उचित फ्रैमरेट पर चल रहे हैं, तो (जिसे फ्रेम की अवधि के अनुसार गणना की जानी चाहिए) काम करने के लिए पर्याप्त रूप से छोटा होगा।L

हालाँकि, आप अत्यधिक मामलों में अशुद्धि का अनुभव कर सकते हैं। यदि बहुत बड़ा है, तो आप गणना को सही तरीके से कर सकते हैं, उदाहरण के लिए 10 भागों का उपयोग करके:L

for (int i = 0; i < 10; i++)
    t = t + (L / 10) / length(t * v1 + v2);

1
नमस्ते। मैं आपके उत्तर को पढ़ रहा हूं, लेकिन मैं यह नहीं समझ सकता कि एल क्या है। आपको क्या मतलब है "जिसे फ्रेम अवधि के अनुसार गणना की जानी चाहिए"?
माइकल IV

क्या एल = वक्र खंड की लंबाई है?
माइकल IV

एल वक्र लंबाई है, यानी वर्तमान फ्रेम के दौरान आप जिस दूरी की यात्रा करना चाहते हैं।
सैम होसेवर

ठीक है, मैं अभी देखता हूं। और आपको लगता है कि यह अनुमान नीचे दिए गए उत्तर से वक्र विभाजन तकनीक जितना अच्छा है?
माइकल IV

जब Lपर्याप्त रूप से छोटा होता है, तो यह अनुमान वास्तव में हमेशा नीचे दिए गए उत्तर की तुलना में अधिक सटीक होता है, हां। यह कम मेमोरी का भी उपयोग करता है (क्योंकि यह सभी बिंदु मानों को संग्रहीत करने के बजाय व्युत्पन्न का उपयोग करता है)। जब Lबढ़ना शुरू होता है, तो आप मेरे द्वारा सुझाई गई तकनीक का उपयोग कर सकते हैं।
सैम होसेवर

6

आपको वक्र को पुन: व्यवस्थित करने की आवश्यकता है। इसका सबसे आसान तरीका यह है कि आप वक्र के कई खंडों की चाप लंबाई की गणना करें और इनका उपयोग यह पता लगाने के लिए करें कि आपको कहाँ से नमूना लेना चाहिए। उदाहरण के लिए, शायद t = 0.5 (आधे रास्ते से) पर, आपको "आधी" स्थिति प्राप्त करने के लिए वक्र के लिए s = 0.7 पास करना चाहिए। आपको ऐसा करने के लिए विभिन्न वक्र खंडों की चाप लंबाई की एक सूची संग्रहीत करने की आवश्यकता है।

शायद बेहतर तरीके हैं, लेकिन यहां कुछ बहुत ही सरल सी # कोड मैंने अपने खेल में ऐसा करने के लिए लिखा है। उद्देश्य C पर पोर्ट करना आसान होना चाहिए:

public sealed class CurveMap<TCurve> where TCurve : struct, ICurve
{
    private readonly float[] _arcLengths;
    private readonly float _ratio;
    public float length { get; private set; }
    public TCurve curve { get; private set; }
    public bool isSet { get { return !length.isNaN(); } }
    public int resolution { get { return _arcLengths.Length; } }

    public CurveMap(int resolution)
    {
        _arcLengths = new float[resolution];
        _ratio = 1f / resolution;
        length = float.NaN;
    }

    public void set(TCurve c)
    {
        curve = c;
        Vector2 o = c.sample(0);
        float ox = o.X;
        float oy = o.Y;
        float clen = 0;
        int nSamples = _arcLengths.Length;
        for(int i = 0; i < nSamples; i++)
        {
            float t = (i + 1) * _ratio;
            Vector2 p = c.sample(t);
            float dx = ox - p.X;
            float dy = oy - p.Y;
            clen += (dx * dx + dy * dy).sqrt();
            _arcLengths[i] = clen;
            ox = p.X;
            oy = p.Y;
        }
        length = clen;
    }

    public Vector2 sample(float u)
    {
        if(u <= 0) return curve.sample(0);
        if(u >= 1) return curve.sample(1);

        int index = 0;
        int low = 0;
        int high = resolution - 1;
        float target = u * length;
        float found = float.NaN;

        // Binary search to find largest value <= target
        while(low < high)
        {
            index = (low + high) / 2;
            found = _arcLengths[index];
            if (found < target)
                low = index + 1;
            else
                high = index;
        }

        // If the value we found is greater than the target value, retreat
        if (found > target)
            index--;

        if(index < 0) return curve.sample(0);
        if(index >= resolution - 1) return curve.sample(1);

        // Linear interpolation for index
        float min = _arcLengths[index];
        float max = _arcLengths[index + 1];
        Debug.Assert(min <= target && max >= target);
        float interp = (target - min) / (max - min);
        Debug.Assert(interp >= 0 && interp <= 1);
        return curve.sample((index + interp + 1) * _ratio);
    }
}

संपादित करें: यह ध्यान देने योग्य है कि यह आपको सटीक चाप लंबाई नहीं देगा, क्योंकि यह क्यूबिक वक्र की चाप लंबाई प्राप्त करना असंभव है। यह सब विभिन्न खंडों की लंबाई का अनुमान लगाता है। वक्र कितना लंबा है, इस पर निर्भर करते हुए, आपको नए सेगमेंट में पहुंचने पर इसे गति को बदलने से रोकने के लिए रिज़ॉल्यूशन को बढ़ाने की आवश्यकता हो सकती है। मैं आमतौर पर ~ 100 का उपयोग करता हूं, जिसके साथ मुझे कभी कोई समस्या नहीं हुई।


0

एक बहुत ही हल्का समाधान वक्र को अनुमानित करने के बजाय गति को अनुमानित करना है। वास्तव में यह दृष्टिकोण वक्र फ़ंक्शन से स्वतंत्र है और आपको डेरिवेटिव या अनुमानों का उपयोग करने के बजाय किसी भी सटीक वक्र का उपयोग करने में सक्षम बनाता है।

यहाँ C # Unity 3D के लिए कोड है:

public float speed; // target linear speed

// determine an initial value by checking where speedFactor converges
float speedFactor = speed / 10; 

float targetStepSize = speed / 60f; // divide by fixedUpdate frame rate
float lastStepSize;

void Update ()
{   
    // Take a note of your previous position.
    Vector3 previousPosition = transform.position;

    // Advance on the curve to the next t;
    transform.position = BezierOrOtherCurveFunction(p0, p1, ..., t);

    // Measure your movement length
    lastStepSize = Vector3.Magnitude(transform.position - previousPosition);

    // Accelerate or decelerate according to your latest step size.
    if (lastStepSize < targetStepSize) 
    {
        speedFactor *= 1.1f;
    }
    else
    {
        speedFactor *= 0.9f;
    }

    t += speedFactor * Time.deltaTime;
}

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


-3

मैं cocos2 के बारे में कुछ नहीं जानता, लेकिन एक बीज़ियर वक्र एक प्रकार का पैरामीट्रिक समीकरण है, इसलिए आपको समय के संदर्भ में अपने x और y मान प्राप्त करने में सक्षम होना चाहिए।


4
एक उदाहरण + अधिक स्पष्टीकरण जोड़ें और यह एक अच्छा जवाब होगा।
माइकल हाउस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.