markus
markus

Reputation: 1011

Bezier not working correctly

I'm trying to implement a simple bezier curve into my Unity game but it no seems to work. here is a screenshot of how is being drawn:

enter image description here

The 4 green spheres are the Bezier's pivots, and the white spheres are the actual bezier being calculated. As you see, its kinda incorrect.

Here's my bezier code (i took it from Unity's forums):

using UnityEngine;

[System.Serializable]
public class Bezier : System.Object
{

    public Vector3 p0;
    public Vector3 p1;
    public Vector3 p2;
    public Vector3 p3;

    public float ti = 0f;

    private Vector3 b0 = Vector3.zero;
    private Vector3 b1 = Vector3.zero;
    private Vector3 b2 = Vector3.zero;
    private Vector3 b3 = Vector3.zero;

    private float Ax;
    private float Ay;
    private float Az;

    private float Bx;
    private float By;
    private float Bz;

    private float Cx;
    private float Cy;
    private float Cz;

    // Init function v0 = 1st point, v1 = handle of the 1st point , v2 = handle of the 2nd point, v3 = 2nd point
    // handle1 = v0 + v1
    // handle2 = v3 + v2
    public Bezier( Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3 )
    {
        this.p0 = v0;
        this.p1 = v1;
        this.p2 = v2;
        this.p3 = v3;
    }

    // 0.0 >= t <= 1.0
    public Vector3 GetPointAtTime( float t )
    {
        this.CheckConstant();
        float t2 = t * t;
        float t3 = t * t * t;
        float x = this.Ax * t3 + this.Bx * t2 + this.Cx * t + p0.x;
        float y = this.Ay * t3 + this.By * t2 + this.Cy * t + p0.y;
        float z = this.Az * t3 + this.Bz * t2 + this.Cz * t + p0.z;
        return new Vector3( x, y, z );

    }

    private void SetConstant()
    {
        this.Cx = 3f * ( ( this.p0.x + this.p1.x ) - this.p0.x );
        this.Bx = 3f * ( ( this.p3.x + this.p2.x ) - ( this.p0.x + this.p1.x ) ) - this.Cx;
        this.Ax = this.p3.x - this.p0.x - this.Cx - this.Bx;

        this.Cy = 3f * ( ( this.p0.y + this.p1.y ) - this.p0.y );
        this.By = 3f * ( ( this.p3.y + this.p2.y ) - ( this.p0.y + this.p1.y ) ) - this.Cy;
        this.Ay = this.p3.y - this.p0.y - this.Cy - this.By;

        this.Cz = 3f * ( ( this.p0.z + this.p1.z ) - this.p0.z );
        this.Bz = 3f * ( ( this.p3.z + this.p2.z ) - ( this.p0.z + this.p1.z ) ) - this.Cz;
        this.Az = this.p3.z - this.p0.z - this.Cz - this.Bz;

    }

    // Check if p0, p1, p2 or p3 have changed
    private void CheckConstant()
    {
        if( this.p0 != this.b0 || this.p1 != this.b1 || this.p2 != this.b2 || this.p3 != this.b3 )
        {
            this.SetConstant();
            this.b0 = this.p0;
            this.b1 = this.p1;
            this.b2 = this.p2;
            this.b3 = this.p3;
        }
    }
}

And here is my implementation script (it's on a empty gameobject in the scene):

public class arrowPath : MonoBehaviour {
    public Transform[] pontos=new Transform[4];
    public Bezier bezier;

    // Use this for initialization
    void Start () {
    }

    // Update is called once per frame
    void Update () {
        bezier=new Bezier(pontos[0].position, pontos[1].position, pontos[2].position, pontos[3].position);

        for(float u= 0.0f; u<=1.0f; u+=0.05f)
        {
            Graphics.DrawMesh(pontos[0].GetComponent<MeshFilter>().sharedMesh, bezier.GetPointAtTime(u), Quaternion.Euler(Vector3.zero), pontos[0].renderer.sharedMaterial, 0);
        }
    }
}

The "pontos" array is Transform array to save the spheres. I fill it via Unity's inspector.

I appreciate any help.

Thanks.

Upvotes: 1

Views: 489

Answers (1)

It looks very much like that GetPointAtTime is intended to generate the Bezier point. If that's the case, you're not evaluating the correct function at all: for a cubic Bezier, the function is:

a ‧ t³ + 3 ‧ b ‧ t² ‧ (1-t) + 3 ‧ c ‧ t ‧ (1-t)² + d ‧ (1-t)³

So your code isn't using binomials, it's simply using a straight polynomial of t which is going to generate really wrong things =)

So, changing your function:

public float ComputeBezierValue( float t, float a, float b, float c, float d )
{
    float t2 = t * t,
          t3 = t2 * t,
          mt = 1 - t,
          mt2 = mt * mt,
          mt3 = mt2 * mt;
    return a * t3 + 3 * b * t2 * mt + 3 * c * t * mt2 + d * mt3;
}

public Vector3 GetPointAtTime( float t )
{
    this.CheckConstant();
    float x = ComputeBezierValue(t, this.Ax, this.Bx, this.Cx, this.Dx);
    float y = ComputeBezierValue(t, this.Ay, this.By, this.Cy, this.Dy);
    float z = ComputeBezierValue(t, this.Az, this.Bz, this.Cz, this.Dz);
    return new Vector3( x, y, z );
}

Also note I've written this to explicitly use four coordinates. Cubic Bezier curves need all four, Quadratic only use three (but are generally very poor for representing uniformly curving segments like circular/elliptical segments, as well as sinoids)

Upvotes: 2

Related Questions