# Bezier Curve

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

1. ### bronxbomber92

Joined:
Nov 11, 2006
Posts:
888
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

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

3. ### 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.

4. ### 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.

5. ### pavees

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

6. ### 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

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

### Volunteer ModeratorModerator

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

--Eric

9. ### 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

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

11. ### 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.

Deleted User and FreebordMAD like this.
12. ### purplelilgirl

Joined:
Feb 24, 2009
Posts:
102
the lazy thanks you

13. ### 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

Joined:
Jan 20, 2011
Posts:
327
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 Deleted User like this.
15. ### 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

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

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

18. ### Tasarran

Joined:
Jan 20, 2011
Posts:
327
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...

19. ### akasurreal

Joined:
Jul 17, 2009
Posts:
441
Thanks for all this, very helpful =)

20. ### Tasarran

Joined:
Jan 20, 2011
Posts:
327
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...

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

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

22. ### 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

Joined:
Jan 20, 2011
Posts:
327
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

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

Joined:
Jan 20, 2011
Posts:
327
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

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

Joined:
Jan 20, 2011
Posts:
327
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

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

Joined:
Jan 20, 2011
Posts:
327
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

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

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

32. ### Tasarran

Joined:
Jan 20, 2011
Posts:
327
So, you're not using the functions in this thread?

33. ### 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

Joined:
Jan 20, 2011
Posts:
327
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

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

36. ### 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

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:Vector3, b:Vector3, c:Vector3, d:Vector3, precision:float){        this.a = a;        aa = (-a + 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; i <= this.len; i++) {            v = bezierPoint(i * precision);            clen += (ov - v).magnitude;            this.arcLengths[i] = clen;            ov = v;        }        this.length = clen;    }    private function map(u:float):float {        var targetLength:float = u * 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* t + (bb))* t + cc)* t + a;    }    public function point(t:float):Vector3{         return bezierPoint( map(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 LTBezierPath( pts_:Vector3[] ){        pts = pts_;                var k:int = 0;        beziers = new LTBezier[ pts.Length / 4 ];        lengthRatio = new float[ beziers.Length ];        for(var i:int=0; i < pts.Length; i+=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(i = 0; i < beziers.Length; i++){            lengthRatio[i] = beziers[i].length / length;        }    }    public function point( ratio:float ):Vector3{        var added:float = 0.0;        for(var i:int = 0; i < lengthRatio.Length; i++){            added += lengthRatio[i];            if(added >= ratio)                return beziers[i].point( (ratio-(added-lengthRatio[i])) / lengthRatio[i] );        }        return beziers[lengthRatio.Length-1].point( 1.0 );;    }    public function place( transform:Transform, ratio:float ){        place( transform, ratio, Vector3.up );    }    public function place( transform:Transform, ratio:float, worldUp:Vector3 ){        transform.position = point( ratio );        ratio += 0.001;        if(ratio<=1.0)            transform.LookAt( point( ratio ), 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.place( lt1.transform, iter1);    lt2.transform.position = ltPath2.point( iter2 );    iter1 += Time.deltaTime*0.1;    if(iter1>1.0)        iter1 = 0.0;    iter2 += Time.deltaTime*0.1;    if(iter2>1.0)        iter2 = 0.0;} ```

LeanTween Editor

Or you can pass in values manually in the format:

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

38. ### 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) .
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.

39. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,834
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

Joined:
Aug 29, 2012
Posts:
11
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
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.         {
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.             {
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.             {
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.                 {
250.                     curPointIndex = 0;
251.                     RectifyCurrentPointEnum(polyBezier);
252.                 }
253.                 rc.x += rc.width;
254.                 if (GUI.Button(rc, "Insert Last"))
255.                 {
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

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

Joined:
Apr 5, 2013
Posts:
381
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

43. ### 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?

44. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,834
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

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.

### Guest

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

Joined:
Jan 20, 2011
Posts:
327
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.

48. ### MikeGDev

Joined:
Dec 19, 2017
Posts:
53
Has anyone made a function that can calculate the t value based on a known point from the curve?

49. ### qibyte

Joined:
Jun 25, 2019
Posts:
4
Your images are so good, help to understand. By the way, in Unity, we can test the Bezier Curves by using Handle like this:

Handles.DrawBezier(p0, p1, new Vector3(1, 0, 3), new Vector3(4,0,1), Color.red, null, 2f);

Last edited: Dec 11, 2022
50. ### qibyte

Joined:
Jun 25, 2019
Posts:
4
Of course, we can get the t value by solving the equation. In Unity, The Bezier curve is:

It's also depending on the kind of Bezier curves you deployed by your self.
For visualization: (from wikipedia)
Linear Bezier curve: