Search Unity

Bezier Curve

Discussion in 'Made With Unity' started by bronxbomber92, May 24, 2007.

  1. bronxbomber92

    bronxbomber92

    Joined:
    Nov 11, 2006
    Posts:
    890
    Hey, I made this small demo to test out an idea for my game. Thought I would share it. I plan on posting the code on the wiki once I add some more flexibility to it :)

    http://bronx.zionmax.com/unity/bezier/
     
  2. Marble

    Marble

    Joined:
    Aug 29, 2005
    Posts:
    1,199
    Looking forward to this! Could see lots of excellent uses.
     
  3. pavees

    pavees

    Joined:
    Jan 1, 2009
    Posts:
    116
    Hi, the link given there has expired so i request u to pls upload the latest and make it accessible.

    thanks in advance
     
  4. Loran

    Loran

    Joined:
    Nov 14, 2007
    Posts:
    124
    If it can help...
    Code (csharp):
    1.  
    2. class Bezier extends System.Object {
    3.    
    4.    
    5.     var p0 : Vector3;
    6.     var p1 : Vector3;
    7.     var p2 : Vector3;
    8.     var p3 : Vector3;
    9.    
    10.     var ti : float = 0.0;
    11.    
    12.     private var b0 : Vector3 = Vector3.zero;
    13.     private var b1 : Vector3 = Vector3.zero;
    14.     private var b2 : Vector3 = Vector3.zero;
    15.     private var b3 : Vector3 = Vector3.zero;
    16.    
    17.     private var Ax : float;
    18.     private var Ay : float;
    19.     private var Az : float;
    20.    
    21.     private var Bx : float;
    22.     private var By : float;
    23.     private var Bz : float;
    24.  
    25.     private var Cx : float;
    26.     private var Cy : float;
    27.     private var Cz : float;
    28.  
    29.     // Init function v0 = 1st point, v1 = handle of the 1st point , v2 = handle of the 2nd point, v3 = 2nd point
    30.     // handle1 = v0 + v1
    31.     // handle2 = v3 + v2
    32.     function Bezier(v0 : Vector3, v1 : Vector3, v2 : Vector3, v3 : Vector3) {
    33.         this.p0 = v0;
    34.         this.p1 = v1;
    35.         this.p2 = v2;
    36.         this.p3 = v3;
    37.     }
    38.    
    39.     // 0.0 >= t <= 1.0
    40.     function GetPointAtTime(t : float) : Vector3 {
    41.         this.CheckConstant();
    42.         var t2 : float = t * t;
    43.         var t3 : float = t * t * t;
    44.         var x : float = this.Ax * t3 + this.Bx * t2 + this.Cx * t + p0.x;
    45.         var y : float = this.Ay * t3 + this.By * t2 + this.Cy * t + p0.y;
    46.         var z : float = this.Az * t3 + this.Bz * t2 + this.Cz * t + p0.z;
    47.         return(Vector3(x,y,z));
    48.        
    49.     }
    50.    
    51.     private function SetConstant() {
    52.         this.Cx = 3 * ((this.p0.x + this.p1.x) - this.p0.x);
    53.         this.Bx = 3 * ((this.p3.x + this.p2.x) - (this.p0.x + this.p1.x)) - this.Cx;
    54.         this.Ax = this.p3.x - this.p0.x - this.Cx - this.Bx;
    55.        
    56.         this.Cy = 3 * ((this.p0.y + this.p1.y) - this.p0.y);
    57.         this.By = 3 * ((this.p3.y + this.p2.y) - (this.p0.y + this.p1.y)) - this.Cy;
    58.         this.Ay = this.p3.y - this.p0.y - this.Cy - this.By;
    59.  
    60.         this.Cz = 3 * ((this.p0.z + this.p1.z) - this.p0.z);
    61.         this.Bz = 3 * ((this.p3.z + this.p2.z) - (this.p0.z + this.p1.z)) - this.Cz;
    62.         this.Az = this.p3.z - this.p0.z - this.Cz - this.Bz;
    63.  
    64.     }
    65.  
    66.     // Check if p0, p1, p2 or p3 have changed
    67.     private function CheckConstant() {
    68.         if (this.p0 != this.b0 || this.p1 != this.b1 || this.p2 != this.b2 || this.p3 != this.b3) {
    69.             this.SetConstant();
    70.             this.b0 = this.p0;
    71.             this.b1 = this.p1;
    72.             this.b2 = this.p2;
    73.             this.b3 = this.p3;
    74.         }
    75.     }
    76. }
    77.  
    Usage:

    Code (csharp):
    1.  
    2. var myBezier : Bezier;
    3. private var t : float = 0.0;
    4.  
    5. function Start() {
    6.     myBezier = new Bezier(Vector3(-5,0,0), Random.insideUnitSphere * 2.0, Random.insideUnitSphere * 2.0, Vector3(5,0,0));
    7. }
    8.  
    9. function Update () {
    10.     var vec : Vector3 = myBezier.GetPointAtTime(t);
    11.     transform.position = vec;
    12.    
    13.     t+= 0.001;
    14.     if (t > 1.0)
    15.         t = 0.0;
    16.    
    17. }
    18.  
     
    FreebordMAD likes this.
  5. pavees

    pavees

    Joined:
    Jan 1, 2009
    Posts:
    116
    Thanks a lot Loran... :) :) :)
     
  6. pavees

    pavees

    Joined:
    Jan 1, 2009
    Posts:
    116
    hi
    i like to thank u again for that help. I hav been able to complete that task of a rope very fin. Its an cool and proper Rope that i was able to make.

    thanks a lot..
    . :)
     
  7. djoscar

    djoscar

    Joined:
    Jul 31, 2009
    Posts:
    141
    why the hell do i keep getting parsing error on that Bezier class? :S
     
  8. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,110
    It's Javascript; maybe you're trying to use it as a C# file?

    --Eric
     
  9. Bastiaens

    Bastiaens

    Joined:
    Oct 8, 2009
    Posts:
    101
    Reup the link if u want please :) so people can see what they are using as script.
     
  10. zorkanerkan

    zorkanerkan

    Joined:
    Jun 2, 2009
    Posts:
    8
    Thank you loran, Good job. it is work.
     
  11. fivedollar

    fivedollar

    Joined:
    Sep 17, 2011
    Posts:
    1
    So I was looking for a unity bezier curve implementation and found this thread! Loran, your code awesome in every aspect, except it is not C#. So I ported it for myself and for the lazy:

    Bezier main class in Bezier.cs:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. [System.Serializable]
    5. public class Bezier : System.Object
    6. {
    7.  
    8.     public Vector3 p0;
    9.     public Vector3 p1;
    10.     public Vector3 p2;
    11.     public Vector3 p3;
    12.  
    13.     public float ti = 0f;
    14.  
    15.     private Vector3 b0 = Vector3.zero;
    16.     private Vector3 b1 = Vector3.zero;
    17.     private Vector3 b2 = Vector3.zero;
    18.     private Vector3 b3 = Vector3.zero;
    19.  
    20.     private float Ax;
    21.     private float Ay;
    22.     private float Az;
    23.  
    24.     private float Bx;
    25.     private float By;
    26.     private float Bz;
    27.  
    28.     private float Cx;
    29.     private float Cy;
    30.     private float Cz;
    31.  
    32.     // Init function v0 = 1st point, v1 = handle of the 1st point , v2 = handle of the 2nd point, v3 = 2nd point
    33.     // handle1 = v0 + v1
    34.     // handle2 = v3 + v2
    35.     public Bezier( Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3 )
    36.     {
    37.         this.p0 = v0;
    38.         this.p1 = v1;
    39.         this.p2 = v2;
    40.         this.p3 = v3;
    41.     }
    42.  
    43.     // 0.0 >= t <= 1.0
    44.     public Vector3 GetPointAtTime( float t )
    45.     {
    46.         this.CheckConstant();
    47.         float t2 = t * t;
    48.         float t3 = t * t * t;
    49.         float x = this.Ax * t3 + this.Bx * t2 + this.Cx * t + p0.x;
    50.         float y = this.Ay * t3 + this.By * t2 + this.Cy * t + p0.y;
    51.         float z = this.Az * t3 + this.Bz * t2 + this.Cz * t + p0.z;
    52.         return new Vector3( x, y, z );
    53.  
    54.     }
    55.  
    56.     private void SetConstant()
    57.     {
    58.         this.Cx = 3f * ( ( this.p0.x + this.p1.x ) - this.p0.x );
    59.         this.Bx = 3f * ( ( this.p3.x + this.p2.x ) - ( this.p0.x + this.p1.x ) ) - this.Cx;
    60.         this.Ax = this.p3.x - this.p0.x - this.Cx - this.Bx;
    61.  
    62.         this.Cy = 3f * ( ( this.p0.y + this.p1.y ) - this.p0.y );
    63.         this.By = 3f * ( ( this.p3.y + this.p2.y ) - ( this.p0.y + this.p1.y ) ) - this.Cy;
    64.         this.Ay = this.p3.y - this.p0.y - this.Cy - this.By;
    65.  
    66.         this.Cz = 3f * ( ( this.p0.z + this.p1.z ) - this.p0.z );
    67.         this.Bz = 3f * ( ( this.p3.z + this.p2.z ) - ( this.p0.z + this.p1.z ) ) - this.Cz;
    68.         this.Az = this.p3.z - this.p0.z - this.Cz - this.Bz;
    69.  
    70.     }
    71.  
    72.     // Check if p0, p1, p2 or p3 have changed
    73.     private void CheckConstant()
    74.     {
    75.         if( this.p0 != this.b0 || this.p1 != this.b1 || this.p2 != this.b2 || this.p3 != this.b3 )
    76.         {
    77.             this.SetConstant();
    78.             this.b0 = this.p0;
    79.             this.b1 = this.p1;
    80.             this.b2 = this.p2;
    81.             this.b3 = this.p3;
    82.         }
    83.     }
    84. }
    85.  
    And an example usage in MyBezier.cs:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class MyBezier : MonoBehaviour
    5. {
    6.     public Bezier myBezier;
    7.     private float t = 0f;
    8.  
    9.     void Start()
    10.     {
    11.         myBezier = new Bezier( new Vector3( -5f, 0f, 0f ), Random.insideUnitSphere * 2f, Random.insideUnitSphere * 2f, new Vector3( 5f, 0f, 0f ) );
    12.     }
    13.  
    14.     void Update()
    15.     {
    16.         Vector3 vec = myBezier.GetPointAtTime( t );
    17.         transform.position = vec;
    18.  
    19.         t += 0.001f;
    20.         if( t > 1f )
    21.             t = 0f;
    22.     }
    23. }
    24.  
    Thank you very much for this.
     
    ScottFromDerby and FreebordMAD like this.
  12. purplelilgirl

    purplelilgirl

    Joined:
    Feb 24, 2009
    Posts:
    102
    the lazy thanks you :)
     
  13. purplelilgirl

    purplelilgirl

    Joined:
    Feb 24, 2009
    Posts:
    102
    the lazy asks (after a month lol) what values should I give the 1st and 2nd handle or the bezier curve?

    thank you.
     
  14. Tasarran

    Tasarran

    Joined:
    Jan 20, 2011
    Posts:
    325
    Well, that depends on the shape you want the curve to be...

    If you've ever used Illustrator and modified curves using those little handlebar things, that is a bezier curve, and the ends of the handlebars are the P1 P2.

    The farther those middle points are from a straight line, the more drastic the curvature.

    I also mean no disrespect to Loran and Fivedollar, but I do have a really bare-bones implementation of the Bezier function, for anyone who wants to take it way down to basics.

    Code (csharp):
    1. function CalculateBezierPoint(t : float,
    2.         p0 : Vector3, p1 : Vector3 , p2 : Vector3 , p3 : Vector3)
    3. {
    4.     var u : float = 1.0 - t;
    5.     var tt : float = t * t;
    6.     var uu : float = u * u;
    7.     var uuu : float = uu * u;
    8.     var ttt : float = tt * t;
    9.  
    10.     var p : Vector3 = uuu * p0; //first term
    11.     p += 3 * uu * t * p1; //second term
    12.     p += 3 * u * tt * p2; //third term
    13.     p += ttt * p3; //fourth term
    14.  
    15.     return p;
    16. }
    Feed it 't' as a 0-1 float representing the point along the curve that you want to get, and the other four points, and it will spit back the point on the curve at that t value.
     
    Griffo and ScottFromDerby like this.
  15. purplelilgirl

    purplelilgirl

    Joined:
    Feb 24, 2009
    Posts:
    102
    No, I have never used Illustrator :( I opened up Blender and created a bezier curve but there was two points on each of the handle bars. The diagram I found in Wikipedia sort of explains it though :)

    Thank you for your bare-bones version :) Will try using that :)
     
  16. DWKSokmeng

    DWKSokmeng

    Joined:
    Jan 26, 2012
    Posts:
    2
    i hope u have free time to explane me about Bezier Curve in unity3d,i'm waiting ur reply
     
  17. DWKSokmeng

    DWKSokmeng

    Joined:
    Jan 26, 2012
    Posts:
    2
    can u explaine me clear me about Bezier Curve in unity 3d clearly then now?
     
  18. Tasarran

    Tasarran

    Joined:
    Jan 20, 2011
    Posts:
    325
    I'll give it a shot...

    A Bezier Curve is a curved line that is defined by four points.
    The first and last points are the ends of the curve, and the two points in the middle distort and define the curvature.
    Here's a picture that might help explain it.

    In this picture, you can see three Bezier Curves of different shapes.
    The P0-P3 points' coordinates are called out, the gray lines are just to help point them out.

    To use my function above, define the curve you want in 3D space, enter in those four points, and a time value 't'.
    't' represents the amount along the curve you want to generate the point for. 0 will return P0, 1 will return P3, .5 will give you the point in the center of the curve.

    The intended use is to use this multiple times in a loop, and either use those points to move or draw something, or store those point in an array for later use...
    $Untitled-1.jpg
     
  19. akasurreal

    akasurreal

    Joined:
    Jul 17, 2009
    Posts:
    439
    Thanks for all this, very helpful =)
     
  20. Tasarran

    Tasarran

    Joined:
    Jan 20, 2011
    Posts:
    325
    You're welcome, I'm glad it is useful!

    And one thing I forgot to mention is that even though my example info there is only 2-D, this function works in 3-D.

    I'm using it right now for flight paths for missiles...
    I start with the beginning and end known, then add an offset to both those points to move P1 P2 up in the +Y direction, run a loop with as many points as I need for precision, and fill an array for my path.

    I also add a small random number to P1 P2, so the missiles don't always fly in a plane between the gun and the target.
    I make sure that I move them both in the same direction, by the same amount, and the effect is basically as if the flight path rotated to the side a few degrees.

    If I applied different numbers, or they were in opposite directions, or the offsets were extreme, the curve could be a really funky 3-D S-curve.
    You can get some pretty interesting curves, even only with four points...

    Edit: added

    And if you want to think about the next level, you can chain multiple bezier curves together to make more complicated paths.
    If you make sure that your P2 from the first curve, the shared P3/P0, and P1 of the second curve are all on the same line (in 3D), the join will be smooth.
    If they are not co-linear, there will be a 'corner' at the join.
    Which I suppose could be a desired outcome in some cases...
     
    Last edited: Feb 5, 2012
  21. ack

    ack

    Joined:
    Feb 24, 2013
    Posts:
    1
    Thanks A lot , your code is really helpfull.. @fivedollar , @Loran
     
  22. LuckyHRF

    LuckyHRF

    Joined:
    May 17, 2013
    Posts:
    6
    I don't know ,what the function "GetPointAtTime" and "SetContent" do. I'm a chinese , English is very poor, sorry . Eventually thanks a lot.
     
  23. Tasarran

    Tasarran

    Joined:
    Jan 20, 2011
    Posts:
    325
    SetConstant is an internal function called by CheckContent...
    GetPointAtTime is what you use to get a point on the curve after you have defined it.
    I assume 0.0f will give you the start of the curve, 1.0f will give you the other end, and numbers in between will give you those points.
    You can run a loop and store a whole array of points, or you can just get them one at a time.

    I think that function is a little too complicated; check out my simpler version lower down, I even have pictures to explain how it works...

    Hope this makes sense... :)
     
  24. LuckyHRF

    LuckyHRF

    Joined:
    May 17, 2013
    Posts:
    6
    I know what you say, but I don't know where the formula come from! That's to say, why "this.Cx = 3 * ((this.p0.x + this.p1.x) - this.p0.x);" atc. It's the Bezier formula? Thank you all right.
     
  25. Tasarran

    Tasarran

    Joined:
    Jan 20, 2011
    Posts:
    325
    Yes, that's the math that's figuring the curve points.
    If you look at my function, you can see the simplest function I could boil it all down to.

    Code (csharp):
    1. function CalculateBezierPoint(t : float,
    2.         p0 : Vector3, p1 : Vector3 , p2 : Vector3 , p3 : Vector3)
    3. {
    4.     var u : float = 1.0 - t;
    5.     var tt : float = t * t;
    6.     var uu : float = u * u;
    7.     var uuu : float = uu * u;
    8.     var ttt : float = tt * t;
    9.  
    10.     var p : Vector3 = uuu * p0; //first term
    11.     p += 3 * uu * t * p1; //second term
    12.     p += 3 * u * tt * p2; //third term
    13.     p += ttt * p3; //fourth term
    14.  
    15.     return p;
    You call the function with five arguments: the four points of your bezier curve (the first and last are the ends, but the middle two might not actually be ON the curve...), and a float between 0 and 1 that is the percentage along the curve you want to get the point for. So 0.5f would get you the midpoint of your curve...
    If you are confused about how the bezier curves work, look at the picture I posted above...

    Oh, and I did not create this math... I can understand what it is doing, but I don't really understand why it works ;)
     
  26. MANISH KUMAR

    MANISH KUMAR

    Joined:
    Nov 22, 2010
    Posts:
    57
    Can anyone who know about , the heading of an object along a curve in unity 3D?
    If possible than help pls.
     
  27. Tasarran

    Tasarran

    Joined:
    Jan 20, 2011
    Posts:
    325
    Well, the way I handle it is I generate all the points for the curve, say an array of 100 points...
    Then I move the object from point to point, and rotate it to LookAt the next point on the curve.
    If you have a smooth curve, you won't need a lot of points; if you have a curve that curves sharply, you'll need more points.
    You'll need to handle what happens when you get to the end, I usually don't actually use the very last point, I stop one short.
     
  28. MANISH KUMAR

    MANISH KUMAR

    Joined:
    Nov 22, 2010
    Posts:
    57
    no its give some jittering problem , i have a solution but not perfect it's close to the perfect, due to 2D
    , mean two dimensional , your are saying about 3D point of view.....
    mean , i apply a point*time = vector;
    than i pass the vector to , rotational operation , but it's giving a problem.
     
  29. Tasarran

    Tasarran

    Joined:
    Jan 20, 2011
    Posts:
    325
    As you move through the points, just have the object rotate to LookAt the point you are moving toward.
    So, if you are at point 0, the object is facing point 1, when you move to point 1, have it facing point 2, etc...
     
  30. MANISH KUMAR

    MANISH KUMAR

    Joined:
    Nov 22, 2010
    Posts:
    57
    yes but giving problem , i did this also., but facing but i give the limit of one axis be zero , than unbehavior result. giving.
    but i convert through this formulae but problem , anyway i post algo of this cocos2D, can you convert this in rotation , in quad bezcurve.
    I post here......
    *************************


    float qx = (powf(1-t,2)*xa + 2*(1-t)*t*xb+powf(t,2)*xc);
    float qy = (powf(1-t,2)*ya + 2*(1-t)*t*yb+powf(t,2)*yc);

    double deltaX = x-qx;
    double deltaY = y-qy;

    double degrees = (-180/M_PI)*ccpToAngle(CGPointMake(deltaX,deltaY));



    **************
    SOMETHING LIKE THIS , JUST ANGLE
    transform.Rotate(angle,0,0);//my required result...
    it's giving a solution as i want , but some problem , try to solved in perfect ,
    Requirement is here in 2D , not in 3D view, rotation along X axis only and movement in y-z axis only...
     
  31. MANISH KUMAR

    MANISH KUMAR

    Joined:
    Nov 22, 2010
    Posts:
    57
    Here is my code , try to help this pls.
    ************************************************
     
  32. Tasarran

    Tasarran

    Joined:
    Jan 20, 2011
    Posts:
    325
    So, you're not using the functions in this thread?
    Sorry, I can't help you.
     
  33. MANISH KUMAR

    MANISH KUMAR

    Joined:
    Nov 22, 2010
    Posts:
    57
    here main thing in 3 line code , how you modify , it's depends on you and more developers,
    if possible than post here .
    ****************************************
    var relative : Vector3 = transform.InverseTransformPoint(2*curve.Interp(inc rement));
    var angle : float = Mathf.Atan2(relative.x, relative.y) * Mathf.Rad2Deg;
    transform.Rotate (angle, 0, 0);
     
  34. Tasarran

    Tasarran

    Joined:
    Jan 20, 2011
    Posts:
    325
    That's not how I would do it at all; I'm not quite sure what is going on with your code, but you don't need to do all that math.
    Here's how I set up my path following:
    I create an array of Vector3s using my code above, looping through, providing t values from 0-1. Call this array BezierPoints.
    I move the object to BezierPoints[0], and use transform.LookAt(BezierPoints[1])
    Then move it to BezierPoints[1], and use
    transform.LookAt(BezierPoints[2]).
    loop through and viola.
     
  35. MANISH KUMAR

    MANISH KUMAR

    Joined:
    Nov 22, 2010
    Posts:
    57
    I think you can't understand my problem,
    arrow heading along a curve without lookAt function.
     
  36. Styked

    Styked

    Joined:
    Jun 5, 2013
    Posts:
    1
    How do I give the heading for the object if I have a simple bezier with just 4 points? The LookAt will work if you have a lot of points and have the object LookAt the next one(like mentioned above by Tasarran), but what about heading with just 4 points?

    Edit: I got it to work. Since Tasarran's function returns a Vector3 value which depends on 't' between 1 and 0, I have the object LookAt the returned value where 't' is "ahead" of the object, i.e. the function receives not t, but t + 0.2 (I have used the value 0.2 but any value can be used, the smaller the value the smoother the result). That gives a smooth heading while the object travels along the bezier.

    I hope this helps anyone who's stuck on the object heading.
     
    Last edited: Jun 5, 2013
  37. dentedpixel

    dentedpixel

    Joined:
    Jul 15, 2012
    Posts:
    683
    I found this thread very helpful when added bezier curve support to my animation engine LeanTween. However a lot of the scripts listed here don't give you the option to integrate over the curve at a constant rate (it will move quickly into the curve and slow down and other strange behavior). I was able to create my own script where you can integrate over the path at a constant rate, you can either use it by downloading my free animation engine LeanTween, or I have also included it here:

    PHP:
    class LTBezier{
        public var 
    length:float;

        private var 
    a:Vector3;
        private var 
    aa:Vector3;
        private var 
    bb:Vector3;
        private var 
    cc:Vector3;
        private var 
    len:float;
        private var 
    arcLengths:float[];

        public function 
    LTBezier(a:Vector3b:Vector3c:Vector3d:Vector3precision:float){
            
    this.a;
            
    aa = (-3*(b-c) + d);
            
    bb 3*(a+c) - 6*b;
            
    cc 3*(b-a);

            
    this.len 1.0 precision;
            
    arcLengths = new float[this.len 1];
            
    arcLengths[0] = 0;

            var 
    ov:Vector3 a;
            var 
    v:Vector3;
            var 
    clen:float 0.0;
            for(var 
    i:int 1<= this.leni++) {
                
    bezierPoint(precision);
                
    clen += (ov v).magnitude;
                
    this.arcLengths[i] = clen;
                
    ov v;
            }
            
    this.length clen;
        }

        private function 
    map(u:float):float {
            var 
    targetLength:float this.arcLengths[this.len];
            var 
    low:int 0;
            var 
    high:int this.len;
            var 
    index:int 0;
            while (
    low high) {
                
    index low + (((high low) / 2) | 0);
                if (
    this.arcLengths[index] < targetLength) {
                    
    low index 1;
                } else {
                    
    high index;
                }
            }
            if(
    this.arcLengths[index] > targetLength)
                
    index--;
            if(
    index<0)
                
    index 0;

            return (
    index + (targetLength arcLengths[index]) / (arcLengths[index 1] - arcLengths[index])) / this.len;
        }

           private function 
    bezierPoint(t:float):Vector3{
            return ((
    aa+ (bb))* cc)* a;
        }

        public function 
    point(t:float):Vector3
            return 
    bezierPointmap(t) ); 
        }
    }

    class 
    LTBezierPath{
        public var 
    pts:Vector3[];
        public var 
    length:float;
        public var 
    orientToPath:boolean;

        private var 
    beziers:LTBezier[];
        private var 
    lengthRatio:float[];
        
        public function 
    LTBezierPathpts_:Vector3[] ){
            
    pts pts_;
            
            var 
    k:int 0;
            
    beziers = new LTBezierpts.Length ];
            
    lengthRatio = new floatbeziers.Length ];
            for(var 
    i:int=0pts.Lengthi+=4){
                
    beziers[k] = new LTBezier(pts[i+0],pts[i+2],pts[i+1],pts[i+3],0.05);
                
    length += beziers[k].length;
                
    k++;
            }
            
    // Debug.Log("beziers.Length:"+beziers.Length + " beziers:"+beziers);
            
    for(0beziers.Lengthi++){
                
    lengthRatio[i] = beziers[i].length length;
            }
        }

        public function 
    pointratio:float ):Vector3{
            var 
    added:float 0.0;
            for(var 
    i:int 0lengthRatio.Lengthi++){
                
    added += lengthRatio[i];
                if(
    added >= ratio)
                    return 
    beziers[i].point( (ratio-(added-lengthRatio[i])) / lengthRatio[i] );
            }
            return 
    beziers[lengthRatio.Length-1].point1.0 );;
        }

        public function 
    placetransform:Transformratio:float ){
            
    placetransformratioVector3.up );
        }

        public function 
    placetransform:Transformratio:floatworldUp:Vector3 ){
            
    transform.position pointratio );
            
    ratio += 0.001;
            if(
    ratio<=1.0)
                
    transform.LookAtpointratio ), worldUp );
        }
    }
    And here is an example of using the class to iterate over a path at constant rate:

    PHP:
    public var editorPath:LeanTweenPath;

    private var 
    lt1:GameObject;
    private var 
    lt2:GameObject;

    private var 
    ltPath1:LTBezierPath;
    private var 
    ltPath2:LTBezierPath;

    private var 
    iter1:float 0.25;
    private var 
    iter2:float 0.5;

    function 
    Start () {
        
    lt1 GameObject.Find("LeanTweenAvatar1");
        
    lt2 GameObject.Find("LeanTweenAvatar2");

        
    ltPath1 = new LTBezierPath(editorPath.path);
        
    ltPath2 = new LTBezierPath(editorPath.path);
    }

    function 
    Update () {
        
    ltPath1.placelt1.transformiter1);
        
    lt2.transform.position ltPath2.pointiter2 );

        
    iter1 += Time.deltaTime*0.1;
        if(
    iter1>1.0)
            
    iter1 0.0;
        
    iter2 += Time.deltaTime*0.1;
        if(
    iter2>1.0)
            
    iter2 0.0;
    }
    For creating paths it is easiest to download the path editor I have made:

    LeanTween Editor

    Or you can pass in values manually in the format:

    new LTBezierPath( startPoint1, controlPoint1, controlPoint2, endPoint2, endPoint2, controlPoint3, controlPoint4, endPoint4, .... );
     
  38. iomismo

    iomismo

    Joined:
    Aug 13, 2013
    Posts:
    1
    Hi everyone, this is awesome. I dont know any maths,but for now ive been able to understand and apply it. But im getting stuck in duplicating objects along the curve.
    A "superbasic" approach to calculate the number of objects to duplicate, was to get the distance between p3 and p0 and divide it by the localScale.x of the duplicate object(is an uniform box). Then create the objects in a for loop, setting "t" time of the curve by dividing the index number of the child object by the total amount of objects to duplicate(to get a 0-1 range) .
    Jeje,sorry about this childish math explanation and to want to understand everything without knowing anything.
    So...

    Code (csharp):
    1. float distance = Vector3.Distance(endDummy.position - startDummy.position);
    2.             distance = Mathf.Round(distance/startDummy.localScale.x) - 1;
    3.             for(int n = 0 ; n < distance;n++){
    4.  
    5.                                 //p1 and p2 are "random" for now
    6.                 Vector3 _pos = CalculateBezierPoint(n/distance,startDummy.position,startDummy.position + (startDummy.position.normalized *5),endDummy.position + (endDummy.position.normalized * 5),endDummy.position);
    7.                 Transform tmpChild = Instantiate(dummy,_pos,Quaternion.identity) as Transform;
    8.             }
    I know this is, again, a childish approach, but if anyone could explain or put me in the right direction,apart from learning maths :), it will be very helpful. I think there is too much maths behind this,but.. who knows. Something like array and curve modifiers in Blender.

    Thanks in advance.
     
  39. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,625
    Anyone struggling to understand how beziers work, or how they can be calculated, should look at the wikipedia page for bezier curves: http://en.wikipedia.org/wiki/Bézier_curve in particular scroll down to the animated gifts which show the De Casteljau algorithm quite simply. It really is basically just a simple subdivision algorithm, finding the mid-point between two sets of coordinates representing the control points and simply linearly interpolating along the lines between the control points. It can be implemented quite efficiently in code, too, and much easier to understand (for me) than than all those squares and cubes etc, even for beziers with more than 4 controls. You don't need to understand complicated formulas or other such math weirdness to understand how the basic algorithm works.
     
  40. Map-Builder

    Map-Builder

    Joined:
    Aug 29, 2012
    Posts:
    10
    Hi,

    I modified the script to make a convenient souple tool. However, there's a problem with the "GetPointAtTime" and "GetTangentAtTime" formulas (in screen bellow, in red : real unity handles.drawbezier math, in yellow : real math). If somebody has an idea, he is welcome.

    After I plan to add a rotation handle to rotate handles arround a node.

    View attachment 85829


    BezierComplex.cs
    // object extension to manage the bezier elements
    (to use it with JS, place make it compile first (place it in a "Standard Assets folder") )

    Code (csharp):
    1. //  If you wanna use that script with JS, it needs to be comiled before JS  script and so placed into a "Standard Assets" folder
    2. //http://forum.unity3d.com/threads/5082-Bezier-Curve
    3.  
    4. using UnityEngine;
    5. using System.Collections;
    6.  
    7. [System.Serializable] // allow storage of variables + display in the inspector to get them back when launching unity.
    8.  
    9. public class BezierComplex : System.Object {
    10.    
    11.     public Vector3[] nodes = new Vector3[0];
    12.     public Vector3[] handlesA = new Vector3[0]; // handle before point
    13.     public Vector3[] handlesB = new Vector3[0]; // hanldesA[0]  hanldesB[max] are unsued
    14.    
    15.     // Bezier constant are stored to calculate progression faster
    16.     //private Vector3[] A = new Vector3[0];
    17.     //private Vector3[] B = new Vector3[0];
    18.     //private Vector3[] C = new Vector3[0];
    19.    
    20.    
    21.     public BezierComplex() {
    22.     }
    23.    
    24.     // t = 0 =---= 1;
    25.     public Vector3 GetPointAtTime(float t) {
    26.         if (t < 0 || t > 1) return Vector3.zero;
    27.         if (nodes.Length < 2) return Vector3.zero;
    28.        
    29.         int seg = GetSegmentAtTime(ref t); // the function will also modify t (ref)
    30.         if (t == 0) return nodes[seg];
    31.        
    32.         return CalculatePosition(t, nodes[seg], handlesB[seg], handlesA[seg+1], nodes[seg+1]);
    33.         //return CalculatePosition(t, nodes[seg], A[seg], B[seg], C[seg]);
    34.     }
    35.    
    36.         public int GetSegmentAtTime(ref float t) { // get segment and retrieve the delta value to t
    37.             int nbSegment = nodes.Length - 1;
    38.             float tRect = t * nbSegment;
    39.             int seg = (int) tRect; // cast to int to get segment
    40.             tRect -= seg; // 0-1 for that segment
    41.             t = tRect;
    42.             return seg;
    43.         }
    44.        
    45.         private Vector3 CalculatePosition(float t, Vector3 p0, Vector3 h0, Vector3 h1, Vector3 p1) {
    46.             float tt = t*t;
    47.             float temp = 1-t;
    48.             float c = 3*temp*tt;
    49.             temp *= temp;
    50.             float b = 3*temp*t;
    51.             temp *= temp;
    52.             float a = temp;
    53.            
    54.             return a * p0 +     b * h0 +             c * h1 +             tt*t * p1;
    55.             // (1-t)^3 p0 +     3(1-t)^2 t p1 +     3(1-t) t^2 p2 +     t^3 p3
    56.         }
    57.        
    58.         /*
    59.         private Vector3 CalculatePosition(float t, Vector3 p0, Vector3 A, Vector3 B, Vector3 C) {
    60.             float t2 = t * t;
    61.             float t3 = t2 * t;
    62.             float x = A.x * t3 + B.x * t2 + C.x * t + p0.x;
    63.             float y = A.y * t3 + B.y * t2 + C.y * t + p0.y;
    64.             float z = A.z * t3 + B.z * t2 + C.z * t + p0.z;
    65.             return new Vector3(x, y, z);
    66.         }//*/
    67.  
    68.     public Vector3 GetTangentAtTime(float t) {
    69.         if (t < 0 || t > 1) return Vector3.zero;
    70.         if (nodes.Length < 2) return Vector3.zero;
    71.         int seg = GetSegmentAtTime(ref t); // the function will also modify t (ref)
    72.         if (t == 0)
    73.         {
    74.             if (seg == nodes.Length-1) return nodes[seg] - handlesA[seg];
    75.             return handlesB[seg] - nodes[seg];
    76.         }
    77.        
    78.         return CalculatePosition(t, nodes[seg], handlesB[seg], handlesA[seg+1], nodes[seg+1]);
    79.     }
    80.    
    81.         private Vector3 CalculateTangent(float t, Vector3 p0, Vector3 h0, Vector3 h1, Vector3 p1) {
    82.            
    83.             float a = 1-t;
    84.             float b = a*6*t;
    85.             a = a*a*3;
    86.             float c = t*t*3;
    87.            
    88.             return     - a * p0  // derivative formula from GetBezierPoint formula
    89.                     + a * h0
    90.                     - b * h0
    91.                     - c * h1
    92.                     + b * h1
    93.                     + c * p1;
    94.             //     - 3(1-t)^2 *P0
    95.             //    + 3(1-t)^2 *P1
    96.             //    - 6t(1-t) * P1
    97.             //    - 3t^2 *     P2
    98.             //    + 6t(1-t) * P2
    99.             //    + 3t^2 *     P3
    100.         }
    101.    
    102.     /*
    103.     // If all bezier is instantaneously created through function
    104.     public void SetAllConstants() {
    105.         for (int i = 0; i < A.Length; i++) SetConstant(i);
    106.     }
    107.    
    108.     private void SetConstant(int i) { // i base node of bezier
    109.         if (i >= A.Length) return; // A.Length = nodes.Length - 1
    110.        
    111.         CalculateConstant(i, nodes[i], handlesB[i], nodes[i+1], handlesA[i+1]);
    112.     }
    113.    
    114.             private void CalculateConstant(int i, Vector3 p0, Vector3 h0, Vector3 p1, Vector3 h1) {
    115.                
    116.                 C[i].x = 3f * ( ( p0.x + h0.x ) - p0.x );
    117.                 B[i].x = 3f * ( ( p1.x + h1.x ) - ( p0.x + h0.x ) ) - C[i].x;
    118.                 A[i].x = p1.x - p0.x - C[i].x - B[i].x;
    119.                
    120.                 C[i].y = 3f * ( ( p0.y + h0.y ) - p0.y );
    121.                 B[i].y = 3f * ( ( p1.y + h1.y ) - ( p0.y + h0.y ) ) - C[i].y;
    122.                 A[i].y = p1.y - p0.y - C[i].y - B[i].y;
    123.                
    124.                 C[i].z = 3f * ( ( p0.z + h0.z ) - p0.z );
    125.                 B[i].z = 3f * ( ( p1.z + h1.z ) - ( p0.z + h0.z ) ) - C[i].z;
    126.                 A[i].z = p1.z - p0.z - C[i].z - B[i].z;
    127.             }
    128.     //*/
    129.    
    130.    
    131.     public Vector3[,] GetBezierInHandlesFormat() {
    132.         // format : (start, end, handleStart, handleEnd)
    133.         int l = GetNumberOfSegment();
    134.         if (l == 0) return null;
    135.        
    136.         Vector3[,] b = new Vector3[l, 4];
    137.         for (int i = 0; i < l; i++)
    138.         {
    139.             b[i, 0] = nodes[i];
    140.             b[i, 1] = nodes[i + 1];
    141.             b[i, 2] = handlesB[i];
    142.             b[i, 3] = handlesA[i + 1];
    143.         }
    144.         return b;
    145.        
    146.     }
    147.    
    148.     /*    Use example
    149.             Vector3[,] h = thisBezier.GetBezierInHandlesFormat(); //Vector3[segments, 4(p0,h0,p1,h1)];
    150.             for (int i = 0; i < l; i++)
    151.             {
    152.                 Handles.DrawBezier(h[i,0], h[i,1], h[i,2], h[i,3], Color.green, null, 2);
    153.             }
    154.     //*/
    155.    
    156.    
    157.     public void DeleteAllNodes() {
    158.            
    159.         nodes = new Vector3[0];
    160.         handlesA = new Vector3[0];
    161.         handlesB = new Vector3[0];
    162.        
    163.         //A = new Vector3[0];
    164.         //B = new Vector3[0];
    165.         //C = new Vector3[0];
    166.        
    167.     }
    168.    
    169.     public void DeleteNode(int i) {
    170.        
    171.          if (i < 0 || i >= nodes.Length) Debug.LogError("You are  trying to DELETE a node which does not exist. Max node index : " +  (nodes.Length - 1) + " . Index wanted : " + i);
    172.        
    173.         ArrayList n = new ArrayList(nodes);
    174.         ArrayList ha = new ArrayList(handlesA);
    175.         ArrayList hb = new ArrayList(handlesB);
    176.         //ArrayList a = new ArrayList(A);
    177.         //ArrayList b = new ArrayList(B);
    178.         //ArrayList c = new ArrayList(C);
    179.        
    180.         n.RemoveAt(i);
    181.         ha.RemoveAt(i);
    182.         hb.RemoveAt(i);
    183.         //a.RemoveAt(i);
    184.         //b.RemoveAt(i);
    185.         //c.RemoveAt(i);
    186.        
    187.         nodes =     (Vector3 []) n.ToArray(typeof(Vector3));
    188.         handlesA =     (Vector3 []) ha.ToArray(typeof(Vector3));
    189.         handlesB =     (Vector3 []) hb.ToArray(typeof(Vector3));
    190.         //A =         (Vector3 []) a.ToArray(typeof(Vector3));
    191.         //B =         (Vector3 []) b.ToArray(typeof(Vector3));
    192.         //C =         (Vector3 []) c.ToArray(typeof(Vector3));
    193.     }
    194.    
    195.    
    196.     public void InsertNode(int i, Vector3 p, Vector3 h1, Vector3 h2) {
    197.        
    198.          if (i < 0 || i > nodes.Length) Debug.LogError("You are  trying to delete a node which does not exist. Max node index (with added  node) : " + nodes.Length + " . Index wanted : " + i);
    199.        
    200.         /*
    201.         string str = "";
    202.         for (int k = 0; k < nodes.Length; k++)
    203.         {
    204.             str += System.Math.Round(nodes[k].x, 0) + ", ";
    205.         }
    206.         Debug.Log(str);
    207.         //*/
    208.        
    209.         ArrayList n = new ArrayList(nodes);
    210.         ArrayList ha = new ArrayList(handlesA);
    211.         ArrayList hb = new ArrayList(handlesB);
    212.         //ArrayList a = new ArrayList(A);
    213.         //ArrayList b = new ArrayList(B);
    214.         //ArrayList c = new ArrayList(C);
    215.        
    216.         if (i == nodes.Length)
    217.         {
    218.             n.Add(p);
    219.             ha.Add(h1);
    220.             hb.Add(h2);
    221.             //a.Add(Vector3.zero);
    222.             //b.Add(Vector3.zero);
    223.             //c.Add(Vector3.zero);
    224.         }
    225.         else
    226.         {
    227.             n.Insert(i, p);
    228.             ha.Insert(i, h1);
    229.             hb.Insert(i, h2);
    230.             //a.Insert(i, Vector3.zero);
    231.             //b.Insert(i, Vector3.zero);
    232.             //c.Insert(i, Vector3.zero);
    233.         }
    234.        
    235.         nodes =     (Vector3 []) n.ToArray(typeof(Vector3));
    236.         handlesA =     (Vector3 []) ha.ToArray(typeof(Vector3));
    237.         handlesB =     (Vector3 []) hb.ToArray(typeof(Vector3));
    238.         //A =         (Vector3 []) a.ToArray(typeof(Vector3));
    239.         //B =         (Vector3 []) b.ToArray(typeof(Vector3));
    240.         //C =         (Vector3 []) c.ToArray(typeof(Vector3));
    241.        
    242.         //if (i != nodes.Length - 1) SetConstant(i);
    243.         //if (i != 0) SetConstant(i - 1);
    244.        
    245.         /*
    246.         for (int k = 0; k < nodes.Length; k++)
    247.         {
    248.             str += System.Math.Round(nodes[k].x, 2) + ", ";
    249.         }
    250.         Debug.Log(str);
    251.         //*/
    252.     }
    253.    
    254.    
    255.     public void MoveNode(Vector3 p, int i) {
    256.          if (i < 0 || i >= nodes.Length) Debug.LogError("You are  trying to MOVE a node which does not exist. Max node index : " +  (nodes.Length - 1) + " . Index wanted : " + i);
    257.         var delta = p - nodes[i];
    258.         handlesA[i] += delta;
    259.         handlesB[i] += delta;
    260.         nodes[i] = p;
    261.        
    262.         //if (i != nodes.Length - 1) SetConstant(i);
    263.         //if (i != 0) SetConstant(i - 1);
    264.     }
    265.    
    266.     public void MoveHandle(Vector3 p, int i, bool handleA) {
    267.          if (i < 0 || i >= nodes.Length) Debug.LogError("You are  trying to MOVE an handle which does not exist. Max node index : " +  (nodes.Length - 1) + " . Index wanted : " + i);
    268.         if (handleA)    handlesA[i] = p;
    269.         else             handlesB[i] = p;
    270.        
    271.         //if (i != nodes.Length - 1) SetConstant(i);
    272.         //if (i != 0) SetConstant(i - 1);
    273.     }
    274.    
    275.    
    276.     public int GetNumberOfSegment() {
    277.         int n = nodes.Length - 1;
    278.         if (n < 0) n = 0;
    279.         return n;
    280.     }
    281.    
    282. }    
    283.    
    284.    
    285.    
    286.    
    287.    
    288.    
    289.    
    290.    
    291.    
    292.    
    293.    
    294.    
    295.    
    296.  

    PolyBezier.cs
    // The script that the othe scripts will interact with. Attached it to an empty gameobject

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class PolyBezier : MonoBehaviour {
    6.    
    7.     public BezierComplex bez = new BezierComplex();
    8.     public float t = 0;
    9.    
    10. }
    11.    
    12.  

    PolyBezierEditor.cs

    // The script that allow you to manage the point through "in editor" gui.

    Code (csharp):
    1.  
    2.  
    3. using UnityEditor;
    4. using UnityEngine;
    5. using System.Collections;
    6. using System;
    7. using System.Reflection;
    8.  
    9. // Thanks to Dantus from Edelweiss Interactive for this trick
    10. public class DefaultHandles
    11. {
    12.     public static bool Hidden
    13.     {
    14.         get
    15.         {
    16.             Type type = typeof(Tools);
    17.             FieldInfo field = type.GetField("s_Hidden", BindingFlags.NonPublic | BindingFlags.Static);
    18.             return ((bool)field.GetValue(null));
    19.         }
    20.         set
    21.         {
    22.             Type type = typeof(Tools);
    23.             FieldInfo field = type.GetField("s_Hidden", BindingFlags.NonPublic | BindingFlags.Static);
    24.             field.SetValue(null, value);
    25.         }
    26.     }
    27. }
    28.  
    29. [CustomEditor(typeof(PolyBezier))]
    30. public class PolyBezierEditor : Editor {
    31.  
    32.     private enum cpte {Node, HandleA, HandleB}; // current point type enum
    33.     private cpte curPointType = cpte.Node;
    34.     int curPointIndex = -1;
    35.     bool hideDefaultHandle = true;
    36.    
    37.     void OnEnable()    { DefaultHandles.Hidden = hideDefaultHandle; }
    38.     void OnDisable()    { DefaultHandles.Hidden = false;}
    39.  
    40.     void MoveNode(PolyBezier polyBezier, Vector3 localPos, int i) {
    41.         polyBezier.bez.MoveNode(localPos, i);
    42.         EditorUtility.SetDirty(target);
    43.     }
    44.    
    45.     void MoveHandle(PolyBezier polyBezier, Vector3 localPos, int i, bool handleA){
    46.         polyBezier.bez.MoveHandle(localPos, i, handleA);
    47.         EditorUtility.SetDirty(target);
    48.     }
    49.    
    50.     void RotateHandles(PolyBezier polyBezier, Quaternion qDelta, int i){
    51.         Vector3 p = polyBezier.bez.nodes[i];
    52.         Vector3 ha = polyBezier.bez.handlesA[i];
    53.         Vector3 hb = polyBezier.bez.handlesB[i];
    54.        
    55.         polyBezier.bez.handlesA[i] = p + qDelta * (ha-p);
    56.         polyBezier.bez.handlesB[i] = p + qDelta * (hb-p);
    57.         EditorUtility.SetDirty(target);
    58.     }
    59.    
    60.     void DeleteNode(PolyBezier polyBezier, int index)
    61.     {
    62.         int nl = polyBezier.bez.nodes.Length;
    63.         if (index < 0 || index >= nl)
    64.             return;
    65.          
    66.         polyBezier.bez.DeleteNode(index);
    67.        
    68.         if (nl == 0)
    69.         {
    70.             curPointIndex = -1;
    71.             RectifyCurrentPointEnum(polyBezier);
    72.         }
    73.         else
    74.         {
    75.             if (index != 0)
    76.             {
    77.                 curPointIndex -= 1;
    78.                 RectifyCurrentPointEnum(polyBezier);
    79.             }
    80.         }
    81.         EditorUtility.SetDirty(target);
    82.     }
    83.  
    84.     void AddNode(PolyBezier polyBezier, int position)
    85.     {
    86.         // before = position
    87.         // after = position + 1
    88.         int nl = polyBezier.bez.nodes.Length;
    89.         Vector3 p;
    90.         Vector3 h;
    91.         Vector3 p2;
    92.         Vector3[] nodes = polyBezier.bez.nodes;
    93.  
    94.         if (nl == 0)
    95.         {
    96.                 p = Vector3.zero;
    97.                 h = Vector3.right * 10;
    98.                 polyBezier.bez.InsertNode(0, p, p+h, p-h);
    99.         }
    100.         else if (nl == 1)
    101.         {
    102.             if (position == 0)
    103.             {
    104.                 p = Vector3.zero;
    105.                 h = Vector3.right * 10;
    106.                 polyBezier.bez.InsertNode(0, p, p+h, p-h);
    107.             }
    108.             else
    109.             {
    110.                 p = nodes[0] * 2;
    111.                 p2 = p - nodes[0];
    112.                
    113.                 polyBezier.bez.InsertNode(    1, p,
    114.                                 polyBezier.bez.handlesA[0] + p2,
    115.                                 polyBezier.bez.handlesB[0] + p2);
    116.             }
    117.         }
    118.         else
    119.         {
    120.            
    121.             if (position == -1 ) return; //position = nl ; // last
    122.  
    123.             if (position == 0)
    124.             {
    125.                 p = nodes[position];
    126.                 p2 = p - nodes[position + 1];
    127.                
    128.                 polyBezier.bez.InsertNode(    position,     p + p2  * 2/4,
    129.                                                         p + p2  * 3/4, // ext
    130.                                                         p + p2  * 1/4 ); // int
    131.             }
    132.             else if ( position == nl)
    133.             {
    134.                 p = nodes[position - 1];
    135.                 p2 = p - nodes[position - 2];
    136.                
    137.                 polyBezier.bez.InsertNode(    position,     p + p2  * 2/4,
    138.                                                         p + p2  * 1/4, // int
    139.                                                         p + p2  * 3/4 ); // ext
    140.             }
    141.             else
    142.             {
    143.                 p = 0.5f * (nodes[position - 1] + nodes[position]); // between point before and after
    144.                  Vector3 pMid = 0.5f * (p - polyBezier.bez.nodes[position -  1]); // this + p = between p and point after, p - this = between point
    145.                
    146.                 polyBezier.bez.InsertNode(    position,     p,
    147.                                                 p - pMid,
    148.                                                 p + pMid);
    149.             }
    150.         }
    151.         EditorUtility.SetDirty(target);
    152.     }
    153.    
    154.     void ResetHandles(PolyBezier polyBezier, int i) {
    155.         Vector3 p = polyBezier.bez.nodes[i];
    156.         polyBezier.bez.handlesA[i] = p - Vector3.right * 4;
    157.         polyBezier.bez.handlesB[i] = p + Vector3.right * 4;
    158.         EditorUtility.SetDirty(target);
    159.     }
    160.    
    161.     void ClearAll(PolyBezier polyBezier) {
    162.         polyBezier.bez.DeleteAllNodes();
    163.     }
    164.  
    165.     void insideSceneGUI(PolyBezier polyBezier)
    166.     {
    167.         Rect size = new Rect(0, 0, 300, 160 + 10);
    168.         float sizeButton = 30;
    169.         Handles.BeginGUI();
    170.       //  Handles.BeginGUI(new Rect(Screen.width - size.width - 10, Screen.height - size.height - 10, size.width, size.height));
    171.        
    172.         GUI.BeginGroup(new Rect(Screen.width - size.width - 10, Screen.height - size.height - 50, size.width, size.height));
    173.         GUI.Box(size, "PolyBezier Tool Bar");
    174.          
    175.          
    176.         Rect rc = new Rect(0, 15, size.width, sizeButton);
    177.          
    178.         GUI.contentColor = Color.black;
    179.         GUI.Label(rc, "Clic on Circles to select a point");
    180.         GUI.contentColor = Color.white;
    181.         rc.y += 15;
    182.         if (curPointIndex != -1)
    183.         {
    184.             GUI.contentColor = Color.black;
    185.             GUI.Label(rc, "Current Point " + curPointIndex);
    186.             GUI.contentColor = Color.white;
    187.            
    188.             rc.y -= 25; // back up
    189.             rc.x = size.width / 5 * 4;
    190.             rc.width = size.width / 5;
    191.             if (GUI.Button(rc, "Deselect"))
    192.             {
    193.                 curPointIndex = -1;
    194.                 RectifyCurrentPointEnum(polyBezier);
    195.             }
    196.             rc.y += 25; // back down
    197.             rc.x = 0;
    198.            
    199.            
    200.            
    201.            
    202.            
    203.             rc.y += 15;
    204.             rc.width = size.width / 3;
    205.            
    206.             if (GUI.Button(rc, "Insert Before"))
    207.             {
    208.                 AddNode(polyBezier, curPointIndex);
    209.             }
    210.             rc.x += rc.width;
    211.            
    212.             if (GUI.Button(rc, "Delete")) DeleteNode(polyBezier, curPointIndex);
    213.             rc.x += rc.width;
    214.            
    215.             if (GUI.Button(rc, "Insert After"))
    216.             {
    217.                 AddNode(polyBezier, curPointIndex + 1);
    218.                 curPointIndex++;
    219.                 RectifyCurrentPointEnum(polyBezier);
    220.             }
    221.            
    222.            
    223.            
    224.            
    225.            
    226.             rc.x = 0;
    227.             rc.y += 10;
    228.             rc.y += sizeButton;
    229.             if (GUI.Button(rc, "Reset Handles"))
    230.             {
    231.                 ResetHandles(polyBezier, curPointIndex);
    232.             }
    233.  
    234.  
    235.         }
    236.         else
    237.         {
    238.             if (polyBezier.bez.nodes.Length == 0)
    239.             {
    240.                 if (GUI.Button(rc, "Insert")) AddNode(polyBezier, 0);
    241.                 rc.y += sizeButton;
    242.             }
    243.             else
    244.             {
    245.                 rc.width = size.width / 2;
    246.  
    247.                 if (GUI.Button(rc, "Insert First"))
    248.                 {
    249.                     AddNode(polyBezier, 0);
    250.                     curPointIndex = 0;
    251.                     RectifyCurrentPointEnum(polyBezier);
    252.                 }
    253.                 rc.x += rc.width;
    254.                 if (GUI.Button(rc, "Insert Last"))
    255.                 {
    256.                     AddNode(polyBezier, polyBezier.bez.nodes.Length);
    257.                     curPointIndex = polyBezier.bez.nodes.Length - 1;
    258.                     RectifyCurrentPointEnum(polyBezier);
    259.                 }
    260.                 rc.y += sizeButton;
    261.             }
    262.         }
    263.  
    264.         if (polyBezier.bez.nodes.Length > 0)
    265.         {
    266.             //rc.y += sizeButton;
    267.             rc.width = size.width / 2;
    268.             rc.x = 0;
    269.             rc.y += sizeButton+10;
    270.             if (GUI.Button(rc, "Clear All"))
    271.             {
    272.                 ClearAll(polyBezier);
    273.                 curPointIndex = -1;
    274.                 RectifyCurrentPointEnum(polyBezier);
    275.             }
    276.         }
    277.          
    278.         rc.x += rc.width;
    279.         if (hideDefaultHandle)
    280.         {
    281.             if (GUI.Button(rc, "Show Main Transform"))
    282.             {
    283.                 hideDefaultHandle = false;
    284.                 DefaultHandles.Hidden = hideDefaultHandle;
    285.             }
    286.         }
    287.         else
    288.         {
    289.             if (GUI.Button(rc, "Hide Main Transform"))
    290.             {
    291.                 hideDefaultHandle = true;
    292.                 DefaultHandles.Hidden = hideDefaultHandle;
    293.             }
    294.         }
    295.  
    296.         GUI.EndGroup();
    297.         Handles.EndGUI();
    298.     }
    299.  
    300.     void OnSceneGUI ()
    301.     {
    302.         PolyBezier polyBezier = (PolyBezier) target;
    303.         DisplayPointsAndLines(polyBezier);
    304.         DisplayBezier(polyBezier);
    305.         DisplayProgression(polyBezier);
    306.        
    307.         SceneView.RepaintAll(); // repaint to display progression
    308.     }
    309.    
    310.         void DisplayBezier(PolyBezier polyBezier) {
    311.            
    312.             int l = polyBezier.bez.GetNumberOfSegment(); // segments
    313.             if (l == 0) return;
    314.             Transform tr = polyBezier.transform;
    315.            
    316.             Vector3[,] h = polyBezier.bez.GetBezierInHandlesFormat(); //Vector3[segments, 4(p0,h0,p1,h1)];
    317.             for (int i = 0; i < l; i++)
    318.             {
    319.                 for (int j = 0; j < 4; j++) h[i, j] = tr.TransformPoint(h[i, j]);
    320.                 Handles.DrawBezier(h[i,0], h[i,1], h[i,2], h[i,3], Color.red, null, 2);
    321.             }
    322.            
    323.             //*
    324.             Handles.color = Color.yellow;
    325.             float tt = 0;
    326.             float step = 0.02f;
    327.            
    328.             Vector3 pb;
    329.             Vector3 pa;
    330.             for (float t = step; t <= 1; t += step)
    331.             {
    332.                 pb = polyBezier.bez.GetPointAtTime(tt);
    333.                 pb = tr.TransformPoint(pb);
    334.                 pa = polyBezier.bez.GetPointAtTime(t);
    335.                 pa = tr.TransformPoint(pa);
    336.                 Handles.DrawLine(pb, pa);
    337.                 tt = t;
    338.             }
    339.             //*/
    340.         }
    341.        
    342.        
    343.         //private Quaternion qRec = Quaternion.identity;
    344.         private Vector3 nodeRec = Vector3.zero;
    345.         private Vector3 hanARec = Vector3.zero;
    346.         private Vector3 hanBRec = Vector3.zero;
    347.        
    348.         void DisplayPointsAndLines(PolyBezier polyBezier) {
    349.        
    350.             int someHashCode = GetHashCode();
    351.              
    352.             Transform tr = polyBezier.transform;
    353.             int nl = polyBezier.bez.nodes.Length;
    354.            
    355.             Vector3[] worldNodes = new Vector3[nl];
    356.             Vector3[] worldHandlesA = new Vector3[nl];
    357.             Vector3[] worldHandlesB = new Vector3[nl];
    358.            
    359.             for (int i = 0; i < nl; i++)
    360.             {  
    361.                 Vector3 node = tr.TransformPoint(polyBezier.bez.nodes[i]);
    362.                 Vector3 hanA = tr.TransformPoint(polyBezier.bez.handlesA[i]);
    363.                 Vector3 hanB = tr.TransformPoint(polyBezier.bez.handlesB[i]);
    364.                
    365.                 //Handles.color = Color.yellow;
    366.                 //Handles.DrawLine(hanA, node);
    367.                 //Handles.DrawLine(node, hanB);
    368.                 //if (i != nl-1) Handles.DrawLine(hanB, tr.TransformPoint(polyBezier.bez.handlesA[i+1]));
    369.                
    370.                 Handles.color = Color.white;
    371.                 Handles.Label(node, "   " + i);
    372.                 float size;
    373.                
    374.             // Display Node and check if it has the focus
    375.             //
    376.                 size = HandleUtility.GetHandleSize(node);
    377.                 SetControlIDState(someHashCode);
    378.                 Handles.ScaleValueHandle(0, node, Quaternion.identity, size, Handles.SphereCap, 0); // display cap
    379.                 if (GetControlIDState(someHashCode))
    380.                 {
    381.                     //Debug.Log(i);
    382.                     curPointIndex = i;
    383.                     curPointType = cpte.Node;
    384.                     RectifyCurrentPointEnum(polyBezier);
    385.                 }
    386.                  
    387.                 Handles.color = Color.blue;
    388.             // Display First Handle and check if it has the focus
    389.             //
    390.                 if (i != 0)
    391.                 {
    392.                     size = HandleUtility.GetHandleSize(hanA);
    393.                     SetControlIDState(someHashCode);
    394.                     Handles.ScaleValueHandle(0, hanA, Quaternion.identity, size, Handles.SphereCap, 0);
    395.                     if (GetControlIDState(someHashCode))
    396.                     {
    397.                         //Debug.Log(i);
    398.                         curPointIndex = i;
    399.                         curPointType = cpte.HandleA;
    400.                         RectifyCurrentPointEnum(polyBezier);
    401.                     }
    402.                     Handles.DrawLine(hanA, node);
    403.                 }
    404.                
    405.             // Display Last Handle and check if it has the focus
    406.             //
    407.                 if (i != nl-1)
    408.                 {
    409.                     size = HandleUtility.GetHandleSize(hanB);
    410.                     SetControlIDState(someHashCode);
    411.                     Handles.ScaleValueHandle(0, hanB, Quaternion.identity, size, Handles.SphereCap, 0);
    412.                     if (GetControlIDState(someHashCode))
    413.                     {
    414.                         //Debug.Log(i);
    415.                         curPointIndex = i;
    416.                         curPointType = cpte.HandleB;
    417.                         RectifyCurrentPointEnum(polyBezier);
    418.                     }
    419.                     Handles.DrawLine(hanB, node);
    420.                 }
    421.            
    422.             // Move points for selected object
    423.             //
    424.                 if (curPointIndex == i)
    425.                 {
    426.                     /*
    427.                     Quaternion q = Handles.RotationHandle(Quaternion.identity, node);
    428.                     if (q != qRec) // do not call EditorUtility.Set Dirty() if nothing is moved
    429.                     {
    430.                         q = q * Quaternion.Inverse(qRec);
    431.                         qRec = q;
    432.                         //RotateHandles(polyBezier, q, curPointIndex); // display rotation gizmo
    433.                     }
    434.                     //*/
    435.                    
    436.                     if (curPointType == cpte.Node)
    437.                     {
    438.                         node = Handles.PositionHandle(node, Quaternion.identity); // display position gizmo
    439.                         if (node != nodeRec) // do not call EditorUtility.Set Dirty() if nothing is moved
    440.                         {
    441.                             MoveNode(polyBezier, tr.InverseTransformPoint(node), i);
    442.                             nodeRec = node;
    443.                         }
    444.                     }
    445.                     else if (curPointType == cpte.HandleA)
    446.                     {
    447.                         hanA = Handles.PositionHandle(hanA, Quaternion.identity);
    448.                         if (hanA != hanARec) // do not call EditorUtility.Set Dirty() if nothing is moved
    449.                         {
    450.                             MoveHandle(polyBezier, tr.InverseTransformPoint(hanA), i, true);
    451.                             hanARec = hanA;
    452.                         }
    453.                     }
    454.                     else
    455.                     {
    456.                         hanB = Handles.PositionHandle(hanB, Quaternion.identity);
    457.                         if (hanB != hanBRec) // do not call EditorUtility.Set Dirty() if nothing is moved
    458.                         {
    459.                             MoveHandle(polyBezier, tr.InverseTransformPoint(hanB), i, false);
    460.                             hanBRec = hanB;
    461.                         }
    462.                     }
    463.                 }
    464.                  
    465.             // Store nodes to display lines
    466.             //
    467.                 worldNodes[i] = node;
    468.                 worldHandlesA[i] = hanA;
    469.                 worldHandlesB[i] = hanB;
    470.                
    471.             }
    472.             insideSceneGUI(polyBezier);
    473.        
    474.             //Handles.color = Color.red;
    475.             //Handles.DrawPolyLine(worldNodes);
    476.            
    477.         }
    478.    
    479.             int controlIDBeforeHandle;
    480.             bool isEventUsedBeforeHandle;
    481.            
    482.             private void SetControlIDState(int hashCode) {
    483.                 // Get the needed data before the handle (selected SphereCap ID)
    484.                 controlIDBeforeHandle = GUIUtility.GetControlID(hashCode, FocusType.Passive);
    485.                 isEventUsedBeforeHandle = (Event.current.type == EventType.used);
    486.             }
    487.        
    488.             private bool GetControlIDState(int hashCode) {
    489.                 // Get the needed data after the handle
    490.                 int controlIDAfterHandle = GUIUtility.GetControlID(hashCode, FocusType.Passive);
    491.                 bool isEventUsedByHandle = !isEventUsedBeforeHandle  (Event.current.type == EventType.used);
    492.      
    493.                  if ( (controlIDBeforeHandle < GUIUtility.hotControl   GUIUtility.hotControl < controlIDAfterHandle) ||  isEventUsedByHandle) return true;
    494.                 return false;
    495.             }
    496.        
    497.         void DisplayProgression(PolyBezier polyBezier) {
    498.             float t = polyBezier.t;
    499.             t = Mathf.Clamp01(t);
    500.             polyBezier.t = t;
    501.             if (t == 0)
    502.             {
    503.                 float tAuto = (Time.realtimeSinceStartup/polyBezier.bez.nodes.Length) % 1;
    504.                 t = tAuto;
    505.             }
    506.            
    507.             Transform tr = polyBezier.transform;
    508.             Vector3 p = polyBezier.bez.GetPointAtTime(t);
    509.             p = tr.TransformPoint(p);
    510.             Vector3 tan = polyBezier.bez.GetTangentAtTime(t);
    511.             tan = tr.TransformDirection(tan);
    512.            
    513.             Handles.color = Color.green;
    514.             Handles.DrawWireDisc(p, tan, 1);
    515.             Handles.DrawLine(p, p + tan * 1);
    516.            
    517.             float tRect = t;
    518.             int segment = polyBezier.bez.GetSegmentAtTime(ref tRect);
    519.             Handles.Label(p,     "     " + Math.Round(t,2) + "\n" +
    520.                                 "segment : " + segment + "\n" +
    521.                                 "local t : " + Math.Round(tRect,2));
    522.            
    523.         }
    524.  
    525.     override public void  OnInspectorGUI()
    526.     {
    527.         base.OnInspectorGUI();
    528.     }
    529.    
    530.     private void RectifyCurrentPointEnum(PolyBezier polyBezier) {
    531.         if (curPointIndex == -1) curPointType = cpte.Node;
    532.          if (curPointIndex == 0                                       curPointType == cpte.HandleA) curPointType = cpte.HandleB;
    533.          if (curPointIndex == polyBezier.bez.nodes.Length - 1       curPointType == cpte.HandleB) curPointType = cpte.HandleA;
    534.     }
    535.  
    536. }
    537.  
    538.  
    539.  
    540.  
    541.  
    542.  
    543.  
    544.  
    545.  
    546.  
    547.  
    548.  
    549.  
    550.  
    551.  
    552.  
    553.  
    554.  
    555.  
    556.  
    557.  
    558.  
    559.  
    560.  
    561.  
    562.  
    563.  
    564.  
    565.  
    566.  
    567.  
     
    Last edited: Feb 7, 2014
    FreebordMAD likes this.
  41. kermado

    kermado

    Joined:
    Feb 25, 2014
    Posts:
    1
    For those asking about orienting the transform to point in the direction of the curve, there is no need to generate a whole load of points. Just evaluate the first derivative of the curve. If you can't perform the differentiation yourself, you can cheat and look-up the equation on the Wikipedia page.
     
  42. silentslack

    silentslack

    Joined:
    Apr 5, 2013
    Posts:
    266
    To fix the GetPointAtTime() function that MapBuilder has provided change the CalculatePosition() function to:

    Code (CSharp):
    1. private Vector3 CalculatePosition(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
    2.     {
    3.         float u = 1.0f - t;
    4.         float tt = t * t;
    5.         float uu = u * u;
    6.         float uuu = uu * u;
    7.         float ttt = tt * t;
    8.         Vector3 p = uuu * p0; //first term
    9.         p += 3 * uu * t * p1; //second term
    10.         p += 3 * u * tt * p2; //third term
    11.         p += ttt * p3; //fourth term
    12.         return p;
    13.     }
    To fix the GetTangentAtTime() make sure the GetTangentAtTime() returns CalculateTangent() and not CalculatePosition().

    To look in direction of spline I use:
    Code (CSharp):
    1. // Look at direction of spline
    2.         Vector3 tangentWorld = mover.transform.position + tangentAlongSpline;
    3.         mover.transform.LookAt(tangentWorld, mover.transform.up);
    Hope that helps
     
    FreebordMAD likes this.
  43. akauper

    akauper

    Joined:
    Jun 16, 2013
    Posts:
    7
    Hey everyone. I know this is an old thread, but I have a question regarding Bezier curves i thought would fit well here.

    Lets say I have a pre-existing Bezier Curve and I want to calculate what the positions of the handles (p1 and p3) would be. Is this possible?

    Thanks for your time.
     
  44. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,625
    you already would know the positions of the start and end handles because those are the same as the first and last bezier points. I think based on reversing the math you should be able to figure out what the other handles were.
     
  45. akauper

    akauper

    Joined:
    Jun 16, 2013
    Posts:
    7
    If anyone has the time I would love some help reversing this math. I've given it a good deal of work but I'm not great with these types of equations.
     
  46. dawnmichal

    dawnmichal

    Joined:
    Apr 4, 2015
    Posts:
    21
    Nice article. But what about when I want to get Y coordinate of point lying on bezier curve when I know the X coordinate of this point? Is this possible?
     
  47. Tasarran

    Tasarran

    Joined:
    Jan 20, 2011
    Posts:
    325
    Well, yes, it's possible, just use algebra, and solve the equation for X instead.
    However, that's a complex equation, and it might just be easier to do a loop that iterates along the curve until the X of the point it calculates is X or close to X and viola.