베 지어 곡선에서 균일 한 이동 속도를 얻는 방법은 무엇입니까?


22

베 지어 곡선을 따라 이미지를 이동하려고합니다. 이것이 내가하는 방법입니다.

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

}

내 문제는 이미지가 균일하게 움직이지 않는다는 것입니다. 처음에는 느리게 움직 인 다음 점차 가속되어 결국에는 아주 빠르게 움직입니다. 이 가속을 제거하려면 어떻게해야합니까?

답변:


27

대부분의 파라 메트릭 궤적에 대해이 문제에 대한 솔루션을 근사화 할 수 있습니다. 아이디어는 다음과 같습니다. 커브를 충분히 깊게 확대하면 해당 시점의 접선에서 커브 자체를 알 수 없습니다.

이 가정을 통해 두 개의 벡터 (입방 베 지어 곡선 등의 세 개) 이상을 미리 계산할 필요가 없습니다 .

따라서 곡선 경우 점에서 접선 벡터 를 계산합니다 . 이 벡터의 표준은 이므로 동안 이동 한 거리는 로 근사 할 수 있습니다 . 그 다음에 거리 이 .()ΔΔ÷

응용 : 이차 베 지어 곡선

베 지어 곡선의 제어점이 , 및 인 경우 궤적은 다음과 같이 표현 될 수 있습니다.에이기음

()=(1)2에이+2(1)+2기음=2(에이2+기음)+(2에이+2)+에이

따라서 파생 상품은 다음과 같습니다.

=(2에이4+2기음)+(2에이+2)

벡터 및 어딘가에 저장하면됩니다. 그런 다음 주어진 에 대해 길이 진행을 원하면 다음을 수행하십시오.V1=2에이4+2기음V2=2에이+2

=+이자형h(V1+V2)

입방 베 지어 곡선

동일한 추론이 네 개의 제어점이 , , 및 곡선에 적용됩니다 .에이기음

M(t)=(1)에이+(1)2+2(1)기음+=(에이+기음+)+2(에이6+기음)+(에이+)+에이

파생 상품은 다음과 같습니다.

=2(에이+99기음+)+(6에이12+6기음)+(에이+)

우리는 세 가지 벡터를 미리 계산합니다.

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
안녕. 귀하의 답변을 읽었지만 L이 무엇인지 이해할 수 없습니다. "프레임 기간에 따라 계산되어야 함"은 무엇을 의미합니까?
Michael IV

L = 커브 세그먼트 길이입니까?
Michael IV

L은 커브 길이, 즉 현재 프레임 동안 이동하려는 거리입니다.
sam hocevar

알았어, 지금 봤어 그리고 당신은이 근사치가 아래 답변의 곡선 분할 기술만큼 좋다고 생각합니까?
Michael IV

L충분히 작고,이 근사 네, 항상 실제로 아래의 답변을보다 더 정확합니다. 또한 모든 포인트 값을 저장하는 대신 미분을 사용하기 때문에 더 적은 메모리를 사용합니다. 때 L성장하기 시작, 당신은 내가 마지막에 제안 기술을 사용할 수 있습니다.
sam hocevar

6

커브를 다시 매개 변수화해야합니다. 가장 쉬운 방법은 곡선의 여러 선분의 호 길이를 계산하고이를 사용하여 샘플링해야하는 위치를 파악하는 것입니다. 예를 들어, t = 0.5 (하프 스루) 인 경우 s = 0.7을 커브에 전달하여 "하프 웨이"위치를 가져와야합니다. 이를 위해 다양한 곡선 세그먼트의 호 길이 목록을 저장해야합니다.

더 좋은 방법이 있을지 모르지만 여기 내 게임에서이 작업을 수행하기 위해 작성한 매우 간단한 C # 코드가 있습니다. 목표 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
예제와 더 많은 설명을 추가하면 좋은 답변이 될 것입니다.
MichaelHouse
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.