RT,馬三最近在參與一款足球遊戲的開發,其中涉及到足球的各類運動軌跡和路徑,好比射門的軌跡,高吊球,香蕉球的軌跡。最先的版本中馬三是使用物理引擎加力的方式實現的足球各類運動,後來的版本中使用了根據物理學公式手動計算位置和物體速度的方式實現,如今這個版本中使用的是DoTween+貝塞爾曲線調節來實現。(關於它們之間的各類優缺點咱們會在之後單獨開一篇博客來探討,屆時也會放出源代碼互相學習下)好了,言歸正傳,今天馬三就來和你們一塊兒學習一下游戲中的貝塞爾曲線以及其在Unity中如何實現。html
貝塞爾曲線是最基本的曲線,通常用在計算機 圖形學和 圖像處理。貝塞爾曲線能夠用來建立平滑的曲線的道路、 彎曲的路徑就像 祖瑪遊戲、 彎曲型的河流等。node
一條貝塞爾曲線是由一組定義的控制點 P0到 Pn,在 n 調用它的順序 (n = 1 爲線性,2 爲二次,等.)。第一個和最後一個控制點老是具備終結點的曲線;然而,中間兩個控制點 (若是有的話) 通常不會位於曲線上 。git
(1)貝塞爾曲線包含兩個控制點即 n = 2 稱爲線性的貝塞爾曲線github
(2)貝塞爾曲線包含三個控制點即 n = 3 稱爲二次貝塞爾曲線數組
(3)貝塞爾曲線包含四個控制點即 n = 4,因此稱爲三次貝塞爾曲線。函數
貝塞爾曲線返回點的貝塞爾函數,使用線性插值的概念做爲基礎。工具
給定點P0、P1,線性貝茲曲線只是一條兩點之間的直線。這條線由下式給出:學習
其等同於線性插值。ui
效果圖(文章中部分圖片轉載自CSDN):spa
二次方貝茲曲線的路徑由給定點P0、P一、P2控制,這條線由下式給出:
效果圖:
N階貝茲曲線可以下推斷。給定點P0、P一、…、Pn,其貝茲曲線即:
經過兩個低階的貝塞爾曲線插值的堆疊總可以得到更高階的貝塞爾曲線,通俗的來講經過對兩條低階的貝塞爾曲線插值,你能夠求得一條高一階的貝塞爾曲線。
好比:二次貝塞爾曲線是點對點的兩個線性貝塞爾曲線的線性插值,三次貝塞爾曲線是兩條二次貝塞爾曲線的線性插值。
效果圖:
經過調節起始點(左邊的白球)、控制點(中間的白球)和結束點(右邊的白球)能夠得到到不一樣的貝塞爾曲線,而後使用LineRender組件將路徑繪製出來,以方便觀察。下面就是實現此功能的代碼:
1 using UnityEngine; 2 using System.Collections.Generic; 3 [RequireComponent(typeof(LineRenderer))] 4 public class Bezier : MonoBehaviour 5 { 6 public Transform[] controlPoints; 7 public LineRenderer lineRenderer; 8 9 private int layerOrder = 0; 10 private int _segmentNum = 50; 11 12 13 void Start() 14 { 15 if (!lineRenderer) 16 { 17 lineRenderer = GetComponent<LineRenderer>(); 18 } 19 lineRenderer.sortingLayerID = layerOrder; 20 } 21 22 void Update() 23 { 24 25 DrawCurve(); 26 27 } 28 29 void DrawCurve() 30 { 31 for (int i = 1; i <= _segmentNum; i++) 32 { 33 float t = i / (float)_segmentNum; 34 int nodeIndex = 0; 35 Vector3 pixel = CalculateCubicBezierPoint(t, controlPoints[nodeIndex].position, 36 controlPoints[nodeIndex+1].position, controlPoints[nodeIndex+2].position); 37 lineRenderer.numPositions = i; 38 lineRenderer.SetPosition(i - 1, pixel); 39 } 40 41 } 42 43 Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2) 44 { 45 float u = 1 - t; 46 float tt = t * t; 47 float uu = u * u; 48 49 Vector3 p = uu * p0; 50 p += 2 * u * t * p1; 51 p += tt * p2; 52 53 return p; 54 } 55 56 }
CalculateCubicBezierPoint()函數負責根據T值計算出對應的貝塞爾曲線中的點,DrawCurve()函數經過不斷的改變T值,並調用CalculateCubicBezierPoint()得到座標點,而後經過LineRenderer將這些點繪製出來。
爲了使用方便,能夠將計算貝賽爾曲線的方法放到一個工具類中——BezierUtils類:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 public class BezierUtils 6 { 7 /// <summary> 8 /// 根據T值,計算貝塞爾曲線上面相對應的點 9 /// </summary> 10 /// <param name="t"></param>T值 11 /// <param name="p0"></param>起始點 12 /// <param name="p1"></param>控制點 13 /// <param name="p2"></param>目標點 14 /// <returns></returns>根據T值計算出來的貝賽爾曲線點 15 private static Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2) 16 { 17 float u = 1 - t; 18 float tt = t * t; 19 float uu = u * u; 20 21 Vector3 p = uu * p0; 22 p += 2 * u * t * p1; 23 p += tt * p2; 24 25 return p; 26 } 27 28 /// <summary> 29 /// 獲取存儲貝塞爾曲線點的數組 30 /// </summary> 31 /// <param name="startPoint"></param>起始點 32 /// <param name="controlPoint"></param>控制點 33 /// <param name="endPoint"></param>目標點 34 /// <param name="segmentNum"></param>採樣點的數量 35 /// <returns></returns>存儲貝塞爾曲線點的數組 36 public static Vector3 [] GetBeizerList(Vector3 startPoint, Vector3 controlPoint, Vector3 endPoint,int segmentNum) 37 { 38 Vector3 [] path = new Vector3[segmentNum]; 39 for (int i = 1; i <= segmentNum; i++) 40 { 41 float t = i / (float)segmentNum; 42 Vector3 pixel = CalculateCubicBezierPoint(t, startPoint, 43 controlPoint, endPoint); 44 path[i - 1] = pixel; 45 Debug.Log(path[i-1]); 46 } 47 return path; 48 49 } 50 }
經過調用 GetBeizerList( )方法就能夠得到到一個包含着計算出的貝塞爾曲線的數組,而後讓Obejct沿着數組裏面的路徑移動就能夠模擬出各類曲線運動的效果了,好比炮彈的飛行軌跡,香蕉球、弧圈球等等各類各樣的曲線效果了,好比下面的效果圖:
博客中貝塞爾曲線工程的開源地址:https://github.com/XINCGer/Unity3DTraining/tree/master/BezierTest
做者:馬三小夥兒
出處:http://www.cnblogs.com/msxh/p/6270468.html 請尊重別人的勞動成果,讓分享成爲一種美德,歡迎轉載。另外,文章在表述和代碼方面若有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!