BezierSpline.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // --------------------------------------------------------------------------------------------------------------------
  2. // <copyright file="Bezier.cs" company="Exit Games GmbH">
  3. // Part of: Photon Unity Networking Demos
  4. // </copyright>
  5. // <summary>
  6. // Original: http://catlikecoding.com/unity/tutorials/curves-and-splines/
  7. // Used in SlotRacer Demo
  8. // </summary>
  9. // <author>developer@exitgames.com</author>
  10. // --------------------------------------------------------------------------------------------------------------------
  11. using UnityEngine;
  12. using System;
  13. namespace Photon.Pun.Demo.SlotRacer.Utils
  14. {
  15. public class BezierSpline : MonoBehaviour
  16. {
  17. [SerializeField]
  18. private Vector3[] points;
  19. [SerializeField]
  20. private float[] lengths;
  21. [SerializeField]
  22. private float[] lengthsTime;
  23. public float TotalLength;
  24. [SerializeField]
  25. private BezierControlPointMode[] modes;
  26. [SerializeField]
  27. private bool loop;
  28. public bool Loop
  29. {
  30. get {
  31. return loop;
  32. }
  33. set {
  34. loop = value;
  35. if (value == true) {
  36. modes[modes.Length - 1] = modes[0];
  37. SetControlPoint(0, points[0]);
  38. }
  39. }
  40. }
  41. public int ControlPointCount
  42. {
  43. get {
  44. return points.Length;
  45. }
  46. }
  47. void Awake()
  48. {
  49. this.ComputeLengths();
  50. }
  51. public Vector3 GetControlPoint(int index)
  52. {
  53. return points[index];
  54. }
  55. public void SetControlPoint(int index, Vector3 point)
  56. {
  57. if (index % 3 == 0)
  58. {
  59. Vector3 delta = point - points[index];
  60. if (loop)
  61. {
  62. if (index == 0)
  63. {
  64. points[1] += delta;
  65. points[points.Length - 2] += delta;
  66. points[points.Length - 1] = point;
  67. }
  68. else if (index == points.Length - 1)
  69. {
  70. points[0] = point;
  71. points[1] += delta;
  72. points[index - 1] += delta;
  73. }
  74. else
  75. {
  76. points[index - 1] += delta;
  77. points[index + 1] += delta;
  78. }
  79. }
  80. else
  81. {
  82. if (index > 0)
  83. {
  84. points[index - 1] += delta;
  85. }
  86. if (index + 1 < points.Length)
  87. {
  88. points[index + 1] += delta;
  89. }
  90. }
  91. }
  92. points[index] = point;
  93. EnforceMode(index);
  94. }
  95. public BezierControlPointMode GetControlPointMode(int index)
  96. {
  97. return modes[(index + 1) / 3];
  98. }
  99. public void SetControlPointMode(int index, BezierControlPointMode mode)
  100. {
  101. int modeIndex = (index + 1) / 3;
  102. modes[modeIndex] = mode;
  103. if (loop)
  104. {
  105. if (modeIndex == 0) {
  106. modes[modes.Length - 1] = mode;
  107. }
  108. else if (modeIndex == modes.Length - 1) {
  109. modes[0] = mode;
  110. }
  111. }
  112. EnforceMode(index);
  113. }
  114. private void EnforceMode(int index)
  115. {
  116. int modeIndex = (index + 1) / 3;
  117. BezierControlPointMode mode = modes[modeIndex];
  118. if (mode == BezierControlPointMode.Free || !loop && (modeIndex == 0 || modeIndex == modes.Length - 1))
  119. {
  120. return;
  121. }
  122. int middleIndex = modeIndex * 3;
  123. int fixedIndex, enforcedIndex;
  124. if (index <= middleIndex)
  125. {
  126. fixedIndex = middleIndex - 1;
  127. if (fixedIndex < 0)
  128. {
  129. fixedIndex = points.Length - 2;
  130. }
  131. enforcedIndex = middleIndex + 1;
  132. if (enforcedIndex >= points.Length)
  133. {
  134. enforcedIndex = 1;
  135. }
  136. }else
  137. {
  138. fixedIndex = middleIndex + 1;
  139. if (fixedIndex >= points.Length)
  140. {
  141. fixedIndex = 1;
  142. }
  143. enforcedIndex = middleIndex - 1;
  144. if (enforcedIndex < 0)
  145. {
  146. enforcedIndex = points.Length - 2;
  147. }
  148. }
  149. Vector3 middle = points[middleIndex];
  150. Vector3 enforcedTangent = middle - points[fixedIndex];
  151. if (mode == BezierControlPointMode.Aligned)
  152. {
  153. enforcedTangent = enforcedTangent.normalized * Vector3.Distance(middle, points[enforcedIndex]);
  154. }
  155. points[enforcedIndex] = middle + enforcedTangent;
  156. }
  157. public int CurveCount
  158. {
  159. get {
  160. return (points.Length - 1) / 3;
  161. }
  162. }
  163. public Vector3 GetPoint(float t)
  164. {
  165. int i;
  166. if (t >= 1f)
  167. {
  168. t = 1f;
  169. i = points.Length - 4;
  170. }
  171. else
  172. {
  173. t = Mathf.Clamp01(t) * CurveCount;
  174. i = (int)t;
  175. t -= i;
  176. i *= 3;
  177. }
  178. return transform.TransformPoint(Bezier.GetPoint(points[i], points[i + 1], points[i + 2], points[i + 3], t));
  179. }
  180. public Vector3 GetVelocity(float t)
  181. {
  182. int i;
  183. if (t >= 1f)
  184. {
  185. t = 1f;
  186. i = points.Length - 4;
  187. }
  188. else
  189. {
  190. t = Mathf.Clamp01(t) * CurveCount;
  191. i = (int)t;
  192. t -= i;
  193. i *= 3;
  194. }
  195. return transform.TransformPoint(Bezier.GetFirstDerivative(points[i], points[i + 1], points[i + 2], points[i + 3], t)) - transform.position;
  196. }
  197. public Vector3 GetDirection(float t)
  198. {
  199. return GetVelocity(t).normalized;
  200. }
  201. public void AddCurve ()
  202. {
  203. Vector3 point = points[points.Length - 1];
  204. Array.Resize(ref points, points.Length + 3);
  205. point.x += 1f;
  206. points[points.Length - 3] = point;
  207. point.x += 1f;
  208. points[points.Length - 2] = point;
  209. point.x += 1f;
  210. points[points.Length - 1] = point;
  211. Array.Resize(ref modes, modes.Length + 1);
  212. modes[modes.Length - 1] = modes[modes.Length - 2];
  213. EnforceMode(points.Length - 4);
  214. if (loop)
  215. {
  216. points[points.Length - 1] = points[0];
  217. modes[modes.Length - 1] = modes[0];
  218. EnforceMode(0);
  219. }
  220. }
  221. public void Reset()
  222. {
  223. points = new Vector3[] {
  224. new Vector3(1f, 0f, 0f),
  225. new Vector3(2f, 0f, 0f),
  226. new Vector3(3f, 0f, 0f),
  227. new Vector3(4f, 0f, 0f)
  228. };
  229. modes = new BezierControlPointMode[] {
  230. BezierControlPointMode.Free,
  231. BezierControlPointMode.Free
  232. };
  233. }
  234. public void ComputeLengths()
  235. {
  236. int subDivisions = 100;
  237. int totalSamples = points.Length * subDivisions;
  238. // lets create lengths for each control point.
  239. this.lengths = new float[totalSamples];
  240. this.lengthsTime = new float[totalSamples];
  241. float totalDistance = 0;
  242. float CurrentTime = 0f;
  243. Vector3 pos;
  244. Vector3 lastPos = this.GetPoint (0f);
  245. // go from the first, to the second to last
  246. for (var i = 0; i < totalSamples - 1; i++)
  247. {
  248. CurrentTime = (1f * i) / totalSamples;
  249. pos = this.GetPoint (CurrentTime);
  250. float _delta = (pos - lastPos).magnitude;
  251. totalDistance += _delta ;
  252. this.lengths [i] = totalDistance;
  253. this.lengthsTime [i] = CurrentTime;
  254. lastPos = pos;
  255. }
  256. this.TotalLength = totalDistance;
  257. }
  258. public Vector3 GetPositionAtDistance(float distance,bool reverse = false)
  259. {
  260. if (reverse)
  261. {
  262. distance = this.TotalLength - distance;
  263. }
  264. distance = Mathf.Repeat (distance, this.TotalLength);
  265. // make sure that we are within the total distance of the points
  266. if(distance <= 0) return points[0];
  267. if(distance >= this.TotalLength) return points[points.Length - 1];
  268. // lets find the first point that is below the distance
  269. // but, who's next point is above the distance
  270. var index = 0;
  271. while (index < lengths.Length -1 && lengths[index] < distance)
  272. index++;
  273. // Debug.Log("Index ="+index);
  274. // get the percentage of travel from the current length to the next
  275. // where the distance is.
  276. //var deltaAmount = Mathf.InverseLerp(lengths[index-1], lengths[index], distance);
  277. float deltaDistanceRatio = (distance-lengths[index-1])/(lengths [index] - lengths [index - 1]) ;
  278. float deltaTime = (lengthsTime [index] - lengthsTime [index - 1]) * deltaDistanceRatio;
  279. //float splineDistance = (lengths [index - 1] + (lengths [index] - lengths [index - 1]) * amount) / this.TotalLength;
  280. return GetPoint(this.lengthsTime[index]+deltaTime);
  281. // we use that, to get the actual point
  282. // return Vector3.Lerp(points[index-1], points[index], amount);
  283. }
  284. }
  285. }