Unity中使用贝塞尔曲线

时间:2023-01-19 05:46:33

鼎鼎大名的贝塞尔曲线相信大家都耳熟能详。这两天因为工作的原因需要将贝塞尔曲线加在工程中,那么MOMO迅速的研究了一下成果就分享给大家了哦。贝塞尔曲线的原理是由两个点构成的任意角度的曲线,这两个点一个是起点,一个是终点。在这条曲线之上还会有两个可以任意移动的点来控制贝塞尔曲线的角度。如下图所示,点1 和点4 就是起点和终点,点2 和点3 就是控制曲线角度的两个动态点。

 

Unity中使用贝塞尔曲线

 

如下图所示。使用拖动条来让曲线发生旋转,大家会看的更加清晰。目前我们看到的被塞尔曲线是在平面中完成的,其实贝塞尔曲线是完全支持3D中完成,这里是为了让大家看的更加清楚MOMO将忽略Z曲线的Z轴。UnityAPI文档中有贝塞尔曲线的方法,可是只支持编辑器中使用,也就是说无法在程序中使用。那么本篇文章我们利用贝塞尔曲线的数学原理以及LineRenderer组件来完成在Unity中使用贝塞尔曲线。

 

Unity中使用贝塞尔曲线

 

创建一个U3D的工程,创建一个新游戏对象,绑定LineRenderer组件。

Bezier.cs 这里是贝塞尔曲线的公式C#版本

 

view source
001 using UnityEngine;
002   
003 [System.Serializable]
004   
005 public class Bezier : System.Object
006   
007 {
008   
009     publicVector3 p0;
010   
011     publicVector3 p1;
012   
013     publicVector3 p2;
014   
015     publicVector3 p3;
016   
017     publicfloat ti = 0f;
018   
019     privateVector3 b0 = Vector3.zero;
020   
021     privateVector3 b1 = Vector3.zero;
022   
023     privateVector3 b2 = Vector3.zero;
024   
025     privateVector3 b3 = Vector3.zero;
026   
027     privatefloat Ax;
028   
029     privatefloat Ay;
030   
031     privatefloat Az;
032   
033     privatefloat Bx;
034   
035     privatefloat By;
036   
037     privatefloat Bz;
038   
039     privatefloat Cx;
040   
041     privatefloat Cy;
042   
043     privatefloat Cz;
044   
045     // Init function v0 = 1st point, v1 = handle of the 1st point , v2 = handle of the 2nd point, v3 = 2nd point
046   
047     // handle1 = v0 + v1
048   
049     // handle2 = v3 + v2
050   
051     publicBezier( Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3 )
052   
053     {
054   
055         this.p0 = v0;
056   
057         this.p1 = v1;
058   
059         this.p2 = v2;
060   
061         this.p3 = v3;
062   
063     }
064   
065     // 0.0 >= t <= 1.0
066   
067     publicVector3 GetPointAtTime( floatt )
068   
069     {
070   
071         this.CheckConstant();
072   
073         floatt2 = t * t;
074   
075         floatt3 = t * t * t;
076   
077         floatx = this.Ax * t3 +this.Bx * t2 +this.Cx * t + p0.x;
078   
079         floaty = this.Ay * t3 +this.By * t2 +this.Cy * t + p0.y;
080   
081         floatz = this.Az * t3 +this.Bz * t2 +this.Cz * t + p0.z;
082   
083         returnnew Vector3( x, y, z );
084   
085     }
086   
087     privatevoid SetConstant()
088   
089     {
090   
091         this.Cx = 3f * ( (this.p0.x +this.p1.x ) -this.p0.x );
092   
093         this.Bx = 3f * ( (this.p3.x +this.p2.x ) - (this.p0.x +this.p1.x ) ) -this.Cx;
094   
095         this.Ax =this.p3.x -this.p0.x -this.Cx - this.Bx;
096   
097         this.Cy = 3f * ( (this.p0.y +this.p1.y ) -this.p0.y );
098   
099         this.By = 3f * ( (this.p3.y +this.p2.y ) - (this.p0.y +this.p1.y ) ) -this.Cy;
100   
101         this.Ay =this.p3.y -this.p0.y -this.Cy - this.By;
102   
103         this.Cz = 3f * ( (this.p0.z +this.p1.z ) -this.p0.z );
104   
105         this.Bz = 3f * ( (this.p3.z +this.p2.z ) - (this.p0.z +this.p1.z ) ) -this.Cz;
106   
107         this.Az =this.p3.z -this.p0.z -this.Cz - this.Bz;
108   
109     }
110   
111     // Check if p0, p1, p2 or p3 have changed
112   
113     privatevoid CheckConstant()
114   
115     {
116   
117         if(this.p0 != this.b0 || this.p1 != this.b1 || this.p2 != this.b2 || this.p3 != this.b3 )
118   
119         {
120   
121             this.SetConstant();
122   
123             this.b0 =this.p0;
124   
125             this.b1 =this.p1;
126   
127             this.b2 =this.p2;
128   
129             this.b3 =this.p3;
130   
131         }
132   
133     }
134   
135 }

 

MyBezier.cs 把它直接挂在摄像机上 ,控制拖动条来控制贝塞尔曲线、

view source
01 using UnityEngine;
02   
03 public class MyBezier : MonoBehaviour
04   
05 {
06   
07     //贝塞尔曲线算法类
08     publicBezier myBezier;
09   
10     //曲线的对象
11     publicGameObject Yellowline;
12   
13     //曲线对象的曲线组件
14     privateLineRenderer YellowlineRenderer;
15   
16     //拖动条用来控制贝塞尔曲线的两个点
17     publicfloat hSliderValue0;
18     publicfloat hSliderValue1;
19   
20     voidStart()
21     {
22         //得到曲线组件
23         YellowlineRenderer = Yellowline.GetComponent<LineRenderer>();
24         //为了让曲线更加美观,设置曲线由100个点来组成
25         YellowlineRenderer.SetVertexCount(100);
26     }
27   
28     voidOnGUI()
29     {
30         //拖动条得出 -5.0 - 5.0之间的一个数值
31         hSliderValue0 = GUI.HorizontalSlider(newRect(25, 25, 100, 30), hSliderValue0, -5.0F, 5.0F);
32         hSliderValue1 = GUI.HorizontalSlider(newRect(25, 70, 100, 30), hSliderValue1, -5.0F, 5.0F);
33     }
34   
35     voidUpdate()
36     {
37         //在这里来计算贝塞尔曲线
38         //四个参数 表示当前贝塞尔曲线上的4个点 第一个点和第四个点
39         //我们是不需要移动的,中间的两个点是由拖动条来控制的。
40         myBezier =new Bezier(new Vector3( -5f, 0f, 0f ), new Vector3( hSliderValue1, hSliderValue0 , 0f ), new Vector3( hSliderValue1, hSliderValue0, 0f ),new Vector3( 5f, 0f, 0f ) );
41   
42         //循环100遍来绘制贝塞尔曲线每个线段
43         for(inti =1; i <= 100; i++)
44         {
45             //参数的取值范围 0 - 1 返回曲线没一点的位置
46             //为了精确这里使用i * 0.01 得到当前点的坐标
47             Vector3 vec = myBezier.GetPointAtTime( (float)(i *0.01) );
48             //把每条线段绘制出来 完成白塞尔曲线的绘制
49             YellowlineRenderer.SetPosition(i -1,vec);
50         }
51   
52     }
53   
54 }

 

OK 这里贝塞尔曲线的原理就已经完毕。下面我们学习在NGUI中如何使用贝塞尔曲线。刚刚我们说过贝塞尔曲线是由2个固定点 加两个动态点来完成的,其实我们在开发中往往只需要3个点。1 起点 2 中间点 3 结束点 拖动这三个点都可以重新计算曲线的轨迹这样才比较完美。如下图所示,这三个点都是可以任意拖动的,拖动结束后,黑色的线为用户拖拽点连接的直角线段,我们根据这三个点组成的直角线段计算它们之间的贝塞尔曲线,也就是图中黄色的线段。

 

Unity中使用贝塞尔曲线

 

简单的进行拖拽一下,是不是感觉贝塞尔曲线很酷炫呢?哇咔咔。

 

Unity中使用贝塞尔曲线

 

我们来看看代码实现的部分,其实原理和上面完全一样。

BallMove.cs绑定在这三个可以拖拽的点上,让拖动小球后小球可跟随手指移动。

view source
01 using UnityEngine;
02 using System.Collections;
03   
04 public class BallMove : MonoBehaviour
05 {
06   
07     voidOnDrag (Vector2 delta)
08     {
09   
10         floatmovex = transform.localPosition.x + (delta.x / 3);
11         floatmovey = transform.localPosition.y + (delta.y / 3);
12          //避免越界操作,这里可以进行一些判断
13         transform.localPosition =new Vector3(movex,movey ,transform.localPosition.z);
14     }
15   
16 }

 

如此一来触摸小球后,小球将跟随用户手指移动。下面我们将监听用户触摸小球后的坐标来计算它们三点之间的贝塞尔曲线。

BallInit.cs挂在摄像机上

view source
01 using UnityEngine;
02 using System.Collections;
03   
04 public class BallInit : MonoBehaviour {
05   
06     //黑色直角线段
07     LineRenderer lineRenderer0;
08     LineRenderer lineRenderer1;
09     //贝塞尔曲线
10     LineRenderer BezierRenderer;
11   
12     //三个小球触摸对象
13     publicGameObject mark0;
14     publicGameObject mark1;
15     publicGameObject mark2;
16   
17     //算法公式类
18     privateBezier myBezier;
19   
20     voidStart ()
21     {
22         //分别得到黑色直角线段 与黄色贝塞尔曲线的 线段组件
23         lineRenderer0 = GameObject.Find("line0").GetComponent<LineRenderer>();
24         lineRenderer1 = GameObject.Find("line1").GetComponent<LineRenderer>();
25         BezierRenderer = GameObject.Find("Bezier").GetComponent<LineRenderer>();
26         //黑色直角是有两个线段组成
27         lineRenderer0.SetVertexCount(2);
28         lineRenderer1.SetVertexCount(2);
29         //为了让贝塞尔曲线细致一些 设置它有100个点组成
30         BezierRenderer.SetVertexCount(100);
31   
32     }
33   
34     voidUpdate ()
35     {
36   
37         //mark0 表示中间的小球
38         //mark1 表示右边的小球
39         //mark2 表示左边的小球
40   
41         //中间的标志点分别减去左右两边的标志点,计算出曲线的X Y 的点
42         floaty = (mark0.transform.position.y  - mark2.transform.position.y)  ;
43         floatx = (mark0.transform.position.x  - mark2.transform.position.x) ; 
44   
45         //因为我们是通过3个点来确定贝塞尔曲线, 所以参数3 设置为0 即可。
46         //这样参数1 表示起点 参数2表示中间点 参数3 忽略 参数4 表示结束点
47         myBezier =new Bezier( mark2.transform.position, new Vector3(x,y,0f), new Vector3(0f,0f,0f), mark1.transform.position );
48   
49         //绘制贝塞尔曲线
50         for(inti =1; i <= 100; i++)
51         {
52             Vector3 vec = myBezier.GetPointAtTime( (float)(i * 0.01) );
53             BezierRenderer.SetPosition(i -1,vec);
54         }
55   
56         //绘制直角黑色标志线段
57         lineRenderer0.SetPosition(0,mark0.transform.position);
58         lineRenderer0.SetPosition(1,mark2.transform.position);
59         lineRenderer1.SetPosition(0,mark0.transform.position);
60         lineRenderer1.SetPosition(1,mark1.transform.position);
61     }
62 }