在Unity设计过程中,我们常常会想让游戏中的人群或者相机延一组点进行移动,若想使其平滑运动,这时我们便会用到贝塞尔曲线。
贝塞尔曲线公式如下:
一条贝塞尔曲线直接使用公式可使两点连接,代码如下:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Curve : MonoBehaviour {
public GameObject[] objs; //要连接的物体
List<Vector3> points; //所有点的位置
public static Material lineMaterial;
static void CreateLineMaterial() {
if (!lineMaterial) {
lineMaterial = new Material("Shader \"Lines/Colored Blended\" {" + "SubShader { Pass { " + " Blend SrcAlpha OneMinusSrcAlpha " + " ZWrite Off Cull Off Fog { Mode Off } " + " BindChannels {" + " Bind \"vertex\", vertex Bind \"color\", color }" + "} } }");
lineMaterial.hideFlags = HideFlags.HideAndDontSave;
lineMaterial.shader.hideFlags = HideFlags.HideAndDontSave;
}
}
void OnPostRender() {
points = Line (objs);
CreateLineMaterial();
lineMaterial.SetPass(0);
GL.Begin(GL.LINES);
GL.Color(Color.red);
//画出贝塞尔曲线
for (int i = 0; i < points.Count - 1; i++) {
GL.Vertex(points[i]);
GL.Vertex(points[i + 1]);
}
//切线
GL.Vertex (objs[0].transform.position);
GL.Vertex (objs[1].transform.position);
GL.Vertex (objs[2].transform.position);
GL.Vertex (objs[3].transform.position);
GL.End();
}
//计算贝塞尔曲线
List<Vector3> Line (GameObject[] objs) {
List<Vector3> line = new List<Vector3> ();
for (float i = 0; i < 1; i+=0.02f) {
Vector3 temp = new Vector3(0,0,0);
for (int j = 0; j < objs.Length; j++) {
temp += nCr (objs.Length - 1,j) * Mathf.Pow((1 - i),objs.Length - 1 - j) * Mathf.Pow(i, j) * objs[j].transform.position;
}
line.Add (temp);
}
return line;
}
//组合
int nCr (int n,int r) {
return Factorial (n) / (Factorial (r) * Factorial (n - r));
}
//阶乘
int Factorial (int value) {
if (value == 0) {
return 1;
}
int sum = 1;
for (int i = 0; i < value; i++) {
sum *= (i + 1);
}
return sum;
}
}
通过这段代码可使两点间的曲线自由变换,其效果如图:
如此,两点间的贝塞尔曲线就做好了。那么现在需要把多条贝塞尔曲线连接到一起,注意,起始点和终点的切线设为0,其余点的切线设为其相邻点的差。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Curve2 : MonoBehaviour {
public GameObject[] objs; //所要连接物体
List<Vector3> points; //所有点的位置
public float tangentSize = 1f; //切线大小
public static Material lineMaterial;
static void CreateLineMaterial() {
if (!lineMaterial) {
lineMaterial = new Material("Shader \"Lines/Colored Blended\" {" + "SubShader { Pass { " + " Blend SrcAlpha OneMinusSrcAlpha " + " ZWrite Off Cull Off Fog { Mode Off } " + " BindChannels {" + " Bind \"vertex\", vertex Bind \"color\", color }" + "} } }");
lineMaterial.hideFlags = HideFlags.HideAndDontSave;
lineMaterial.shader.hideFlags = HideFlags.HideAndDontSave;
}
}
void OnPostRender() {
Vector3[] pos = new Vector3[objs.Length];
for (int i = 0; i < objs.Length; i++) {
pos[i] = objs[i].transform.position;
}
points = WholeLine (pos);
CreateLineMaterial();
lineMaterial.SetPass(0);
GL.Begin(GL.LINES);
GL.Color(Color.red);
for (int i = 0; i < points.Count - 1; i++) {
GL.Vertex(points[i]);
GL.Vertex(points[i + 1]);
}
GL.End();
}
//画出所有曲线
List<Vector3> WholeLine (Vector3[] value) {
List<Vector3> line = new List<Vector3> ();
Vector3[] tempPoint = new Vector3[4];
for (int i = 0; i < value.Length - 1; i++) {
tempPoint[0] = value[i];
if (i == 0) {
tempPoint[1] = value[i];
} else {
tempPoint[1] = value[i] + Vector3.Normalize(value[i + 1] - value[i - 1]) * tangentSize;
}
tempPoint[3] = value[i + 1];
if (i == value.Length - 2) {
tempPoint[2] = value[i + 1];
} else {
tempPoint[2] = value[i + 1] + Vector3.Normalize(value[i] - value[i + 2]) * tangentSize;
}
List<Vector3> temp = Line (tempPoint);
for (int j = 0; j < temp.Count; j++) {
line.Add(temp[j]);
}
}
return line;
}
//画出单条曲线
List<Vector3> Line (Vector3[] p) {
List<Vector3> line = new List<Vector3> ();
for (float i = 0; i < 1; i+=0.02f) {
Vector3 temp = new Vector3(0,0,0);
for (int j = 0; j < p.Length; j++) {
temp += nCr (p.Length - 1,j) * Mathf.Pow((1 - i),p.Length - 1 - j) * Mathf.Pow(i, j) * p[j];
}
line.Add (temp);
}
return line;
}
//组合
int nCr (int n,int r) {
return Factorial (n) / (Factorial (r) * Factorial (n - r));
}
//阶乘
int Factorial (int value) {
if (value == 0) {
return 1;
}
int sum = 1;
for (int i = 0; i < value; i++) {
sum *= (i + 1);
}
return sum;
}
}
多条贝塞尔曲线效果图:
用这样的曲线去模拟人群行走,就会变得平滑许多啦!