一提到曲线,很多新手就头疼了,包括我。查了很多资料,终于有个大概的了解。想深入了解曲线原理的,推荐一个链接http://www.cnblogs.com/jay-dong/archive/2012/09/26/2704188.html
之前写了一篇博文《unity3D:游戏分解之角色移动和相机跟随》,里面用到了曲线插值,这里算是对上篇博文的一个补充
先看一下曲线的效果
在使用NGUI的过程中,发现iTween.cs里面有两个很有用的方法,一个是输入指定路点数组,一个就是曲线的插值算法。今天我们主要就用到这两个方法来实现曲线效果。
1 public static Vector3[] PathControlPointGenerator(Vector3[] path) 2 { 3 Vector3[] suppliedPath; 4 Vector3[] vector3s; 5 6 //create and store path points: 7 suppliedPath = path; 8 9 //populate calculate path; 10 int offset = 2; 11 vector3s = new Vector3[suppliedPath.Length + offset]; 12 Array.Copy(suppliedPath, 0, vector3s, 1, suppliedPath.Length); 13 14 //populate start and end control points: 15 //vector3s[0] = vector3s[1] - vector3s[2]; 16 vector3s[0] = vector3s[1] + (vector3s[1] - vector3s[2]); 17 vector3s[vector3s.Length - 1] = vector3s[vector3s.Length - 2] + (vector3s[vector3s.Length - 2] - vector3s[vector3s.Length - 3]); 18 19 //is this a closed, continuous loop? yes? well then so let's make a continuous Catmull-Rom spline! 20 if (vector3s[1] == vector3s[vector3s.Length - 2]) 21 { 22 Vector3[] tmpLoopSpline = new Vector3[vector3s.Length]; 23 Array.Copy(vector3s, tmpLoopSpline, vector3s.Length); 24 tmpLoopSpline[0] = tmpLoopSpline[tmpLoopSpline.Length - 3]; 25 tmpLoopSpline[tmpLoopSpline.Length - 1] = tmpLoopSpline[2]; 26 vector3s = new Vector3[tmpLoopSpline.Length]; 27 Array.Copy(tmpLoopSpline, vector3s, tmpLoopSpline.Length); 28 } 29 30 return (vector3s); 31 } 32 33 //andeeee from the Unity forum's steller Catmull-Rom class ( http://forum.unity3d.com/viewtopic.php?p=218400#218400 ): 34 public static Vector3 Interp(Vector3[] pts, float t) 35 { 36 int numSections = pts.Length - 3; 37 int currPt = Mathf.Min(Mathf.FloorToInt(t * (float)numSections), numSections - 1); 38 float u = t * (float)numSections - (float)currPt; 39 40 if(currPt == 0) 41 { 42 int dsd = 0; 43 } 44 45 Vector3 a = pts[currPt]; 46 Vector3 b = pts[currPt + 1]; 47 Vector3 c = pts[currPt + 2]; 48 Vector3 d = pts[currPt + 3]; 49 50 return .5f * ( 51 (-a + 3f * b - 3f * c + d) * (u * u * u) 52 + (2f * a - 5f * b + 4f * c - d) * (u * u) 53 + (-a + c) * u 54 + 2f * b 55 ); 56 }
直接上完整代码,把这个脚本放到相机上,然后在场景中拖几个物件作为路点,就可以实现上面的效果
1 using System; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 namespace Fish.Study.Curve 6 { 7 /// <summary> 8 /// 曲线测试 9 /// </summary> 10 public class CurveTest : MonoBehaviour 11 { 12 //路点 13 public GameObject[] GameObjectList; 14 //各路点的坐标 15 public List<Vector3> TransDataList = new List<Vector3>(); 16 17 void Start() 18 { 19 } 20 21 //Gizmos 22 void OnDrawGizmos() 23 { 24 //1个点是不能画出曲线的,2个点实际上是直线 25 if (GameObjectList.Length <= 1) return; 26 27 TransDataList.Clear(); 28 for (int i = 0; i < GameObjectList.Length; ++i) 29 { 30 TransDataList.Add(GameObjectList[i].transform.position); 31 } 32 33 if (TransDataList != null && TransDataList.Count > 1) 34 { 35 DrawPathHelper(TransDataList.ToArray(), Color.red); 36 } 37 } 38 39 public Vector3[] GetCurveData() 40 { 41 if (TransDataList != null && TransDataList.Count > 1) 42 { 43 var v3 = (TransDataList.ToArray()); 44 Vector3[] vector3s = PathControlPointGenerator(v3); 45 return vector3s; 46 } 47 48 return null; 49 } 50 51 //NGUI iTween.cs中的方法,输入路径点 52 public static Vector3[] PathControlPointGenerator(Vector3[] path) 53 { 54 Vector3[] suppliedPath; 55 Vector3[] vector3s; 56 57 //create and store path points: 58 suppliedPath = path; 59 60 //populate calculate path; 61 int offset = 2; 62 vector3s = new Vector3[suppliedPath.Length + offset]; 63 Array.Copy(suppliedPath, 0, vector3s, 1, suppliedPath.Length); 64 65 //populate start and end control points: 66 vector3s[0] = vector3s[1] + (vector3s[1] - vector3s[2]); 67 vector3s[vector3s.Length - 1] = vector3s[vector3s.Length - 2] + (vector3s[vector3s.Length - 2] - vector3s[vector3s.Length - 3]); 68 69 //is this a closed, continuous loop? yes? well then so let's make a continuous Catmull-Rom spline! 70 if (vector3s[1] == vector3s[vector3s.Length - 2]) 71 { 72 Vector3[] tmpLoopSpline = new Vector3[vector3s.Length]; 73 Array.Copy(vector3s, tmpLoopSpline, vector3s.Length); 74 tmpLoopSpline[0] = tmpLoopSpline[tmpLoopSpline.Length - 3]; 75 tmpLoopSpline[tmpLoopSpline.Length - 1] = tmpLoopSpline[2]; 76 vector3s = new Vector3[tmpLoopSpline.Length]; 77 Array.Copy(tmpLoopSpline, vector3s, tmpLoopSpline.Length); 78 } 79 80 return (vector3s); 81 } 82 83 //曲线插值函数 84 public static Vector3 Interp(Vector3[] pts, float t) 85 { 86 int numSections = pts.Length - 3; 87 int currPt = Mathf.Min(Mathf.FloorToInt(t * (float)numSections), numSections - 1); 88 float u = t * (float)numSections - (float)currPt; 89 90 Vector3 a = pts[currPt]; 91 Vector3 b = pts[currPt + 1]; 92 Vector3 c = pts[currPt + 2]; 93 Vector3 d = pts[currPt + 3]; 94 95 return .5f * ( 96 (-a + 3f * b - 3f * c + d) * (u * u * u) 97 + (2f * a - 5f * b + 4f * c - d) * (u * u) 98 + (-a + c) * u 99 + 2f * b 100 ); 101 } 102 103 //画曲线 104 private void DrawPathHelper(Vector3[] path, Color color) 105 { 106 Vector3[] vector3s = PathControlPointGenerator(path); 107 108 //Line Draw: 109 Vector3 prevPt = Interp(vector3s, 0); 110 int SmoothAmount = path.Length * 20; 111 for (int i = 1; i <= SmoothAmount; i++) 112 { 113 float pm = (float)i / SmoothAmount; 114 Vector3 currPt = Interp(vector3s, pm); 115 116 Gizmos.color = color; 117 Gizmos.DrawSphere(currPt, 0.2f); 118 prevPt = currPt; 119 } 120 } 121 } 122 }