কীভাবে বেজিয়ার বক্ররেখা অবিচ্ছিন্ন গতি অর্জন করতে?


22

আমি বেজিয়ার কার্ভ বরাবর একটি চিত্র সরানোর চেষ্টা করছি। আমি এটি এইভাবে করি:

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

}

আমার সমস্যাটি হচ্ছে চিত্রটি অভিন্নভাবে চলে না। শুরুতে এটি ধীরে ধীরে এগিয়ে চলেছে এবং তারপরে এটি ধীরে ধীরে ত্বরান্বিত হয় এবং শেষে এটি সত্যিই দ্রুত গতিতে চলেছে। এই ত্বরণ থেকে মুক্তি পেতে আমার কী করা উচিত?

উত্তর:


27

বেশিরভাগ প্যারামেট্রিক ট্র্যাজেক্টোরিগুলির জন্য এই সমস্যার সমাধানটি প্রায় অনুমান করা সম্ভব। ধারণাটি নিম্নরূপ: আপনি যদি কোনও বাঁকায় গভীরভাবে জুম করেন তবে আপনি সেই মুহুর্তে বাঁকটিকে তার স্পর্শক থেকে নিজেকে বলতে পারবেন না।

এই অনুমানটি তৈরি করে, দুটি ভেক্টর (কিউবিক বেজিয়ার কার্ভের জন্য তিনটি ) ইত্যাদির চেয়ে বেশি কিছু করার দরকার নেই ।

সুতরাং একটি বক্ররেখা জন্য এম(টি) আমরা তার স্পর্শক ভেক্টর গনা এমটিটিএমটিΔটিএমটিΔটিএলএল÷এমটি

অ্যাপ্লিকেশন: চতুর্ভুজ বেজিয়ার বক্ররেখা

যদি বেজিয়ার কার্ভের কন্ট্রোল পয়েন্টগুলি , এবং তবে ট্রাজেক্টোরিটি এইভাবে প্রকাশ করা যেতে পারে:ABC

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

সুতরাং ডেরাইভেটিভ হ'ল:

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

আপনার কেবল ভেক্টর এবং কোথাও সঞ্চয় করতে হবে। তারপরে, প্রদত্ত , আপনি যদি দৈর্ঘ্যের অগ্রসর হতে চান তবে আপনি এটি করুন:v1=2A4B+2Cv2=2A+2BtL

টি=টি+ +এলএনটি(টিবনাম1+ +বনাম2)

কিউবিক বেজিয়ার কার্ভগুলি

একই যুক্তি চারটি নিয়ন্ত্রণ পয়েন্ট , , এবং সহ একটি বক্ররেখার সাথে প্রযোজ্য :একজনBCD

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

এবং চূড়ান্ত সূত্রটি হ'ল:

টি=টি+ +এলএনটি(টি2বনাম1+ +টিবনাম2+ +বনাম3)

নির্ভুলতার বিষয়গুলি

আপনি যদি কোনও যুক্তিসঙ্গত ফ্রেমরেটে চলেছেন তবে (যা ফ্রেমের সময়কাল অনুসারে গণনা করা উচিত) কাজ করার জন্য আনুমানিক জন্য যথেষ্ট ছোট হবে।এল

যাইহোক, আপনি চরম ক্ষেত্রে ক্ষেত্রে ভুল ব্যবহার করতে পারেন। যদি খুব বড় হয়, আপনি 10 অংশ ব্যবহার করে উদাহরণস্বরূপ গণনাটি করতে পারেন:এল

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

1
হাই। আমি আপনার উত্তরটি পড়ছি, তবে আমি বুঝতে পারছি না এল কী "" ফ্রেমের সময়কাল অনুসারে গণনা করা উচিত "দ্বারা আপনি কী বোঝেন?
মাইকেল চতুর্থ

এল = বক্রাকার বিভাগের দৈর্ঘ্য কি?
মাইকেল চতুর্থ

এল হ'ল বাঁক দৈর্ঘ্য, অর্থাৎ বর্তমান ফ্রেমের সময় আপনি যে দূরত্বটি ভ্রমণ করতে চান তা হ'ল।
সাম হোসেভার

ঠিক আছে, আমি এখন দেখতে। এবং আপনি কি মনে করেন যে এই অনুমানটি নীচের উত্তর থেকে বক্র বিভাজন কৌশল হিসাবে ভাল?
মাইকেল চতুর্থ

যখন Lপর্যাপ্ত পরিমাণে ছোট হয় তখন এই আনুমানিকটি নীচের উত্তরের চেয়ে সবসময়ই আরও সঠিক হয়, হ্যাঁ। এটি কম স্মৃতিও ব্যবহার করে (কারণ এটি সমস্ত পয়েন্টের মান সংরক্ষণের পরিবর্তে ডেরাইভেটিভ ব্যবহার করে)। যখন Lবাড়তে শুরু করে, আপনি শেষে প্রস্তাবিত কৌশলটি ব্যবহার করতে পারেন।
সাম হোসেভার

6

আপনার বক্ররেখার পুনঃনির্ধারণ করা দরকার। এটি করার সহজতম উপায় হ'ল বক্রাকার কয়েকটি বিভাগের চাপের দৈর্ঘ্য গণনা করা এবং আপনার কোথা থেকে নমুনা করা উচিত তা নির্ধারণের জন্য এগুলি ব্যবহার করুন। উদাহরণস্বরূপ, হতে পারে t = 0.5 এ (অর্ধেক পথ দিয়ে), "অর্ধেক" অবস্থান পাওয়ার জন্য আপনাকে বক্ররেখা = 0.7 দিয়ে যেতে হবে। এটি করার জন্য আপনাকে বিভিন্ন বক্ররেগমেন্টের তোরণ দৈর্ঘ্যের একটি তালিকা সংরক্ষণ করতে হবে।

সম্ভবত আরও ভাল উপায় আছে তবে এখানে আমার গেমটিতে এটি করার জন্য আমি খুব সহজ কিছু সি # কোড লিখেছি। উদ্দেশ্য সি তে পোর্ট করা সহজ হওয়া উচিত:

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

একটি খুব লাইটওয়েট সলিউশনটি হ'ল বক্ররেখার পরিবর্তে গতি আনুমানিক করা। প্রকৃতপক্ষে এই পদ্ধতিটি কার্ভ ফাংশন থেকে স্বতন্ত্র এবং ডেরাইভেটিভস বা আনুমানিকতা ব্যবহার না করে আপনাকে কোনও সঠিক বক্র ব্যবহার করতে সক্ষম করে।

সি # ইউনিটি 3 ডি এর কোড এখানে:

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

আমি কোকোস 2 সম্পর্কে কিছুই জানি না, তবে বেজিয়ার বক্ররেখা হ'ল এক ধরণের প্যারাম্যাট্রিক সমীকরণ, তাই আপনার সময়ের সাথে আপনার এক্স এবং ওয়াইয়ের মানগুলি অর্জন করতে সক্ষম হওয়া উচিত।


4
একটি উদাহরণ + আরও ব্যাখ্যা যুক্ত করুন এবং এটি একটি ভাল উত্তর হবে।
মাইকেলহাউস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.