鼎鼎大名的貝塞爾曲線相信大家都耳熟能詳。這兩天因為工作的原因需要將貝塞爾曲線加在工程中,那么MOMO迅速的研究了一下成果就分享給大家了哦。貝塞爾曲線的原理是由兩個點構成的任意角度的曲線,這兩個點一個是起點,一個是終點。在這條曲線之上還會有兩個可以任意移動的點來控制貝塞爾曲線的角度。如下圖所示,點1 和點4 就是起點和終點,點2 和點3 就是控制曲線角度的兩個動態點。
如下圖所示。使用拖動條來讓曲線發生旋轉,大家會看的更加清晰。目前我們看到的被塞爾曲線是在平面中完成的,其實貝塞爾曲線是完全 支持3D中完成,這里是為了讓大家看的更加清楚MOMO將忽略Z曲線的Z軸。UnityAPI文檔中有貝塞爾曲線的方法,可是只支持編輯器中使用,也就是 說無法在程序中使用。那么本篇文章我們利用貝塞爾曲線的數學原理以及LineRenderer組件來完成在Unity中使用貝塞爾曲線。
創建一個U3D的工程,創建一個新游戲對象,綁定LineRenderer組件。
Bezier.cs 這里是貝塞爾曲線的公式C#版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
using UnityEngine;
[System.Serializable]
public class Bezier : System.Object
{
public Vector3 p0;
public Vector3 p1;
public Vector3 p2;
public Vector3 p3;
public float ti = 0f;
private Vector3 b0 = Vector3.zero;
private Vector3 b1 = Vector3.zero;
private Vector3 b2 = Vector3.zero;
private Vector3 b3 = Vector3.zero;
private float Ax;
private float Ay;
private float Az;
private float Bx;
private float By;
private float Bz;
private float Cx;
private float Cy;
private float Cz;
// Init function v0 = 1st point, v1 = handle of the 1st point , v2 = handle of the 2nd point, v3 = 2nd point
// handle1 = v0 + v1
// handle2 = v3 + v2
public Bezier( Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3 )
{
this.p0 = v0;
this.p1 = v1;
this.p2 = v2;
this.p3 = v3;
}
// 0.0 >= t <= 1.0
public Vector3 GetPointAtTime( float t )
{
this.CheckConstant();
float t2 = t * t;
float t3 = t * t * t;
float x = this.Ax * t3 + this.Bx * t2 + this.Cx * t + p0.x;
float y = this.Ay * t3 + this.By * t2 + this.Cy * t + p0.y;
float z = this.Az * t3 + this.Bz * t2 + this.Cz * t + p0.z;
return new Vector3( x, y, z );
}
private void SetConstant()
{
this.Cx = 3f * ( ( this.p0.x + this.p1.x ) - this.p0.x );
this.Bx = 3f * ( ( this.p3.x + this.p2.x ) - ( this.p0.x + this.p1.x ) ) - this.Cx;
this.Ax = this.p3.x - this.p0.x - this.Cx - this.Bx;
this.Cy = 3f * ( ( this.p0.y + this.p1.y ) - this.p0.y );
this.By = 3f * ( ( this.p3.y + this.p2.y ) - ( this.p0.y + this.p1.y ) ) - this.Cy;
this.Ay = this.p3.y - this.p0.y - this.Cy - this.By;
this.Cz = 3f * ( ( this.p0.z + this.p1.z ) - this.p0.z );
this.Bz = 3f * ( ( this.p3.z + this.p2.z ) - ( this.p0.z + this.p1.z ) ) - this.Cz;
this.Az = this.p3.z - this.p0.z - this.Cz - this.Bz;
}
// Check if p0, p1, p2 or p3 have changed
private void CheckConstant()
{
if( this.p0 != this.b0 || this.p1 != this.b1 || this.p2 != this.b2 || this.p3 != this.b3 )
{
this.SetConstant();
this.b0 = this.p0;
this.b1 = this.p1;
this.b2 = this.p2;
this.b3 = this.p3;
}
}
}
|
MyBezier.cs 把它直接掛在攝像機上 ,控制拖動條來控制貝塞爾曲線、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
using UnityEngine;
public class MyBezier : MonoBehaviour
{
//貝塞爾曲線算法類
public Bezier myBezier;
//曲線的對象
public GameObject Yellowline;
//曲線對象的曲線組件
private LineRenderer YellowlineRenderer;
//拖動條用來控制貝塞爾曲線的兩個點
public float hSliderValue0;
public float hSliderValue1;
void Start()
{
//得到曲線組件
YellowlineRenderer = Yellowline.GetComponent<LineRenderer>();
//為了讓曲線更加美觀,設置曲線由100個點來組成
YellowlineRenderer.SetVertexCount(100);
}
void OnGUI()
{
//拖動條得出 -5.0 - 5.0之間的一個數值
hSliderValue0 = GUI.HorizontalSlider(new Rect(25, 25, 100, 30), hSliderValue0, -5.0F, 5.0F);
hSliderValue1 = GUI.HorizontalSlider(new Rect(25, 70, 100, 30), hSliderValue1, -5.0F, 5.0F);
}
void Update()
{
//在這里來計算貝塞爾曲線
//四個參數 表示當前貝塞爾曲線上的4個點 第一個點和第四個點
//我們是不需要移動的,中間的兩個點是由拖動條來控制的。
myBezier = new Bezier( new Vector3( -5f, 0f, 0f ), new Vector3( hSliderValue1, hSliderValue0 , 0f ), new Vector3( hSliderValue1, hSliderValue0, 0f ), new Vector3( 5f, 0f, 0f ) );
//循環100遍來繪制貝塞爾曲線每個線段
for(int i =1; i <= 100; i++)
{
//參數的取值范圍 0 - 1 返回曲線沒一點的位置
//為了精確這里使用i * 0.01 得到當前點的坐標
Vector3 vec = myBezier.GetPointAtTime( (float)(i *0.01) );
//把每條線段繪制出來 完成白塞爾曲線的繪制
YellowlineRenderer.SetPosition(i -1,vec);
}
}
}
|
OK 這里貝塞爾曲線的原理就已經完畢。下面我們學習在NGUI中如何使用貝塞爾曲線。剛剛我們說過貝塞爾曲線是由2個固定點 加兩個動態點來完成的,其實我們在開發中往往只需要3個點。1 起點 2 中間點 3 結束點 拖動這三個點都可以重新計算曲線的軌跡這樣才比較完美。如下圖所示,這三個點都是可以任意拖動的,拖動結束后,黑色的線為用戶拖拽點連接的直角線段,我們 根據這三個點組成的直角線段計算它們之間的貝塞爾曲線,也就是圖中黃色的線段。
簡單的進行拖拽一下,是不是感覺貝塞爾曲線很酷炫呢?哇咔咔。
我們來看看代碼實現的部分,其實原理和上面完全一樣。
BallMove.cs綁定在這三個可以拖拽的點上,讓拖動小球后小球可跟隨手指移動。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
using UnityEngine;
using System.Collections;
public class BallMove : MonoBehaviour
{
void OnDrag (Vector2 delta)
{
float movex = transform.localPosition.x + (delta.x / 3);
float movey = transform.localPosition.y + (delta.y / 3);
//避免越界操作,這里可以進行一些判斷
transform.localPosition = new Vector3(movex,movey ,transform.localPosition.z);
}
}
|
如此一來觸摸小球后,小球將跟隨用戶手指移動。下面我們將監聽用戶觸摸小球后的坐標來計算它們三點之間的貝塞爾曲線。
BallInit.cs掛在攝像機上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
using UnityEngine;
using System.Collections;
public class BallInit : MonoBehaviour {
//黑色直角線段
LineRenderer lineRenderer0;
LineRenderer lineRenderer1;
//貝塞爾曲線
LineRenderer BezierRenderer;
//三個小球觸摸對象
public GameObject mark0;
public GameObject mark1;
public GameObject mark2;
//算法公式類
private Bezier myBezier;
void Start ()
{
//分別得到黑色直角線段 與黃色貝塞爾曲線的 線段組件
lineRenderer0 = GameObject.Find("line0").GetComponent<LineRenderer>();
lineRenderer1 = GameObject.Find("line1").GetComponent<LineRenderer>();
BezierRenderer = GameObject.Find("Bezier").GetComponent<LineRenderer>();
//黑色直角是有兩個線段組成
lineRenderer0.SetVertexCount(2);
lineRenderer1.SetVertexCount(2);
//為了讓貝塞爾曲線細致一些 設置它有100個點組成
BezierRenderer.SetVertexCount(100);
}
void Update ()
{
//mark0 表示中間的小球
//mark1 表示右邊的小球
//mark2 表示左邊的小球
//中間的標志點分別減去左右兩邊的標志點,計算出曲線的X Y 的點
float y = (mark0.transform.position.y - mark2.transform.position.y) ;
float x = (mark0.transform.position.x - mark2.transform.position.x) ;
//因為我們是通過3個點來確定貝塞爾曲線, 所以參數3 設置為0 即可。
//這樣參數1 表示起點 參數2表示中間點 參數3 忽略 參數4 表示結束點
myBezier = new Bezier( mark2.transform.position, new Vector3(x,y,0f), new Vector3(0f,0f,0f), mark1.transform.position );
//繪制貝塞爾曲線
for(int i =1; i <= 100; i++)
{
Vector3 vec = myBezier.GetPointAtTime( (float)(i * 0.01) );
BezierRenderer.SetPosition(i -1,vec);
}
//繪制直角黑色標志線段
lineRenderer0.SetPosition(0,mark0.transform.position);
lineRenderer0.SetPosition(1,mark2.transform.position);
lineRenderer1.SetPosition(0,mark0.transform.position);
lineRenderer1.SetPosition(1,mark1.transform.position);
}
}
|