Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to animate a bezier curve over a given duration

Tags:

c#

unity3d

I have created a bezier curve by adding the following script to an empty game object in the inspector. This draws to complete curve at once when I run the code. How can I animate it over a given period of time, say 2 or 3 seconds?

public class BCurve : MonoBehaviour {

LineRenderer lineRenderer;
public Vector3 point0, point1, point2;
int numPoints = 50;
Vector3[] positions = new Vector3[50];

// Use this for initialization
void Start () {
    lineRenderer = gameObject.AddComponent<LineRenderer>();
    lineRenderer.material = new Material (Shader.Find ("Sprites/Default"));
    lineRenderer.startColor = lineRenderer.endColor = Color.white;
    lineRenderer.startWidth = lineRenderer.endWidth = 0.1f;
    lineRenderer.positionCount = numPoints;

    DrawQuadraticCurve ();

}

void DrawQuadraticCurve ()  {
    for (int i = 1; i < numPoints + 1; i++) {
        float t = i / (float)numPoints;
        positions [i - 1] = CalculateLinearBeziearPoint (t, point0, point1, point2);

    }
    lineRenderer.SetPositions(positions);
}

Vector3 CalculateLinearBeziearPoint (float t, Vector3 p0, Vector3 p1, Vector3 p2)   {

    float u = 1 - t;
    float tt = t * t;
    float uu = u * u;
    Vector3 p = uu * p0 + 2 * u * t * p1 + tt * p2;

    return p;
}

}
like image 707
chronos Avatar asked Nov 12 '17 13:11

chronos


People also ask

How do I extend my Bezier curve?

You can extend an open Bézier path. To do so, briefly click one of the path's two end points with the Bézier tool while holding down the SHIFT key. You can then add further Bézier points by clicking. Bézier paths can be connected to other Bézier paths and also to lines, rectangles, polygons and ellipses.

How are Bézier curves used in animation?

In animation applications, such as Adobe Flash and Synfig, Bézier curves are used to outline, for example, movement. Users outline the wanted path in Bézier curves, and the application creates the needed frames for the object to move along the path.


1 Answers

Use a coroutine:

public class BCurve : MonoBehaviour {

    LineRenderer lineRenderer;
    public Vector3 point0, point1, point2;
    int numPoints = 50;
    Vector3[] positions = new Vector3[50];

    // Use this for initialization
    void Start () {
        lineRenderer = gameObject.AddComponent<LineRenderer>();
        lineRenderer.material = new Material (Shader.Find ("Sprites/Default"));
        lineRenderer.startColor = lineRenderer.endColor = Color.white;
        lineRenderer.startWidth = lineRenderer.endWidth = 0.1f;

        StartCoroutine(DrawQuadraticCurve (3));

    }

    IEnumerator DrawQuadraticCurve (float duration)  {
        //Calculate wait duration for each loop so it match 3 seconds
        float waitDur = duration / numPoints;

        for (int i = 1; i < numPoints + 1; i++) {
            float t = i / (float)numPoints;
            lineRenderer.positionCount = i;
            lineRenderer.setPosition(i - 1, CalculateLinearBeziearPoint (t, point0, point1, point2));
            yield return new WaitForSeconds(waitDur);
        }
    }

    Vector3 CalculateLinearBeziearPoint (float t, Vector3 p0, Vector3 p1, Vector3 p2)   {

        float u = 1 - t;
        float tt = t * t;
        float uu = u * u;
        Vector3 p = uu * p0 + 2 * u * t * p1 + tt * p2;

        return p;
    }

}

EDIT: Added a specific duration to the call.

Note: If duration / numPoints is likely to be less than Time.deltaTime, you may want to use this method instead, which can draw multiple points per frame:

    IEnumerator DrawQuadraticCurve (float duration)  {
        float progressPerSecond = 1 / duration;
        float startTime = Time.time;
        float progress = 0;
        while (progress < 1) {
            progress = Mathf.clamp01((Time.time - startTime) * progressPerSecond);
            int prevPointCount = lineRenderer.positionCount;
            int curPointCount = progress * numPoints;
            lineRenderer.positionCount = curPointCount;
            for (int i = prevPointCount; i < curPointCount; ++i) {
                float t = i / (float)numPoints;
                lineRenderer.setPosition(i, CalculateLinearBeziearPoint (t, point0, point1, point2));
            }
            yield return null;
        }
    }
like image 93
Ed Marty Avatar answered Oct 28 '22 07:10

Ed Marty