SimpleBezierCurvePath.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using QFramework;
  4. using Org.BouncyCastle.Bcpg.OpenPgp;
  5. #if UNITY_EDITOR
  6. using UnityEditor;
  7. using Sirenix.OdinInspector;
  8. #endif
  9. namespace SK.Framework
  10. {
  11. /// <summary>
  12. /// 贝塞尔曲线路径
  13. /// </summary>
  14. public class SimpleBezierCurvePath : MonoBehaviour
  15. {
  16. [SerializeField] private BezierCurve curve;
  17. public float pathMaxDistance;
  18. [ContextMenu("刷新投影")]
  19. public void Refrush()
  20. {
  21. RaycastHit hit = new RaycastHit();
  22. targetPoints.Clear();
  23. //步长
  24. float step = 1f / curve.segments;
  25. //缓存上个坐标点
  26. Vector3 lastPos = transform.TransformPoint(curve.EvaluatePosition(0f));
  27. float end = (curve.points.Count - 1 < 1 ? 0 : (curve.loop ? curve.points.Count : curve.points.Count - 1)) + step * .5f;
  28. for (float t = step; t <= end; t += step)
  29. {
  30. //计算位置
  31. Vector3 p = transform.TransformPoint(curve.EvaluatePosition(t));
  32. if (Physics.Raycast(new Ray(p, targetPoint - p), out hit, 10))
  33. {
  34. targetPoints.Add(new MeshPoint(this.transform.InverseTransformPoint(hit.point), this.transform.InverseTransformDirection(hit.normal), 0));
  35. }
  36. }
  37. pathMaxDistance = 0;
  38. for (int t = 0; t < targetPoints.Count; t++)
  39. {
  40. if (t == 0)
  41. {
  42. lastPos = transform.TransformPoint(targetPoints[t].position);
  43. targetPoints[t].SetCurrentDistance(pathMaxDistance);
  44. continue;
  45. }
  46. Vector3 p = transform.TransformPoint(targetPoints[t].position);
  47. pathMaxDistance += Vector3.Distance(lastPos, p);
  48. targetPoints[t].SetCurrentDistance(pathMaxDistance);
  49. lastPos = p;
  50. }
  51. }
  52. public List<MeshPoint> targetPoints = new List<MeshPoint>();
  53. public bool Loop { get { return curve.loop; } }
  54. public Vector3 targetPoint;
  55. public List<BezierCurvePoint> Points { get { return curve.points; } }
  56. /// <summary>
  57. /// 根据归一化位置值获取对应的贝塞尔曲线上的点
  58. /// </summary>
  59. /// <param name="t">归一化位置值 [0,1]</param>
  60. /// <returns></returns>
  61. public Vector3 EvaluatePosition(float t, bool worldPos = true)
  62. {
  63. if (worldPos)
  64. {
  65. return transform.TransformPoint(curve.EvaluatePosition(t));
  66. }
  67. else
  68. {
  69. return curve.EvaluatePosition(t);
  70. }
  71. }
  72. [HideInInspector]
  73. public MeshPoint meshPoint;
  74. public MeshPoint GetPos(float t)
  75. {
  76. float targetflag = pathMaxDistance * t;
  77. for (int i = 0; i < targetPoints.Count; i++)
  78. {
  79. if (targetPoints[i].currentDistance > targetflag)
  80. {
  81. float l = targetPoints[i - 1].currentDistance / pathMaxDistance;
  82. float s = targetPoints[i].currentDistance / pathMaxDistance;
  83. float q = (t - l) / (s - l) * Vector3.Distance(targetPoints[i].position, targetPoints[i - 1].position);
  84. Vector3 point = transform.TransformPoint(targetPoints[i - 1].position + q * (targetPoints[i].position - targetPoints[i - 1].position).normalized);
  85. Vector3 normail = transform.TransformDirection(targetPoints[i - 1].normail + q * (targetPoints[i].normail - targetPoints[i - 1].normail)).normalized;
  86. meshPoint.position = point;
  87. meshPoint.normail = normail;
  88. return meshPoint;
  89. }
  90. }
  91. meshPoint.position = Vector3.zero;
  92. meshPoint.normail = Vector3.zero;
  93. return meshPoint;
  94. }
  95. #if UNITY_EDITOR
  96. /// <summary>
  97. /// 路径颜色(Gizmos)
  98. /// </summary>
  99. public Color pathColor = Color.green;
  100. public Color projectColor = Color.yellow;
  101. private void OnDrawGizmos()
  102. {
  103. if (Selection.activeGameObject != this.gameObject) return;
  104. if (curve.points.Count == 0) return;
  105. //缓存颜色
  106. Color cacheColor = Gizmos.color;
  107. //路径绘制颜色
  108. Gizmos.color = pathColor;
  109. //步长
  110. float step = 1f / curve.segments;
  111. //缓存上个坐标点
  112. Vector3 lastPos = transform.TransformPoint(curve.EvaluatePosition(0f));
  113. float end = (curve.points.Count - 1 < 1 ? 0 : (curve.loop ? curve.points.Count : curve.points.Count - 1)) + step * .5f;
  114. for (float t = step; t <= end; t += step)
  115. {
  116. //计算位置
  117. Vector3 p = transform.TransformPoint(curve.EvaluatePosition(t));
  118. //绘制曲线
  119. Gizmos.DrawLine(lastPos, p);
  120. Gizmos.color = new Color(1, 1, 1, 0.2f);
  121. Gizmos.DrawLine(p, targetPoint);
  122. Gizmos.color = pathColor;
  123. //记录
  124. lastPos = p;
  125. }
  126. Vector3 lastNormail = Vector3.zero;
  127. for (int t = 0; t < targetPoints.Count; t++)
  128. {
  129. if (t == 0)
  130. {
  131. lastPos = transform.TransformPoint(targetPoints[t].position);
  132. lastNormail = transform.TransformPoint(targetPoints[t].position) + transform.TransformDirection(targetPoints[t].normail).normalized * 0.03f;
  133. continue;
  134. }
  135. Vector3 p = transform.TransformPoint(targetPoints[t].position);
  136. Gizmos.DrawLine(lastPos, p);
  137. Gizmos.color = projectColor;
  138. Vector3 nomailPoint = p + transform.TransformDirection(targetPoints[t].normail).normalized * 0.03f;
  139. Gizmos.DrawLine(p, nomailPoint);
  140. Gizmos.DrawLine(lastNormail, nomailPoint);
  141. lastPos = p;
  142. lastNormail = nomailPoint;
  143. Gizmos.color = projectColor;
  144. }
  145. //恢复颜色
  146. Gizmos.color = cacheColor;
  147. }
  148. #endif
  149. }
  150. #if UNITY_EDITOR
  151. [CustomEditor(typeof(SimpleBezierCurvePath))]
  152. public class SimpleBezierCurvePathEditor : Editor
  153. {
  154. private SimpleBezierCurvePath path;
  155. private const float sphereHandleCapSize = .2f;
  156. private void OnEnable()
  157. {
  158. path = target as SimpleBezierCurvePath;
  159. }
  160. private void OnSceneGUI()
  161. {
  162. //路径点集合为空
  163. if (path.Points == null || path.Points.Count == 0) return;
  164. //当前选中工具非移动工具
  165. if (Tools.current != Tool.Move) return;
  166. //颜色缓存
  167. Color cacheColor = Handles.color;
  168. Handles.color = Color.yellow;
  169. DrawTargetPointHandle();
  170. //遍历路径点集合
  171. for (int i = 0; i < path.Points.Count; i++)
  172. {
  173. DrawPositionHandle(i);
  174. DrawTangentHandle(i);
  175. BezierCurvePoint point = path.Points[i];
  176. //局部转全局坐标 路径点、控制点
  177. Vector3 position = path.transform.TransformPoint(point.position);
  178. Vector3 controlPoint = path.transform.TransformPoint(point.position + point.tangent);
  179. //绘制切线
  180. Handles.DrawDottedLine(position, controlPoint, 1f);
  181. }
  182. //恢复颜色
  183. Handles.color = cacheColor;
  184. }
  185. //路径点操作柄绘制
  186. private void DrawPositionHandle(int index)
  187. {
  188. BezierCurvePoint point = path.Points[index];
  189. //局部转全局坐标
  190. Vector3 position = path.transform.TransformPoint(point.position);
  191. //操作柄的旋转类型
  192. Quaternion rotation = Tools.pivotRotation == PivotRotation.Local
  193. ? path.transform.rotation : Quaternion.identity;
  194. //操作柄的大小
  195. float size = HandleUtility.GetHandleSize(position) * sphereHandleCapSize;
  196. //在该路径点绘制一个球形
  197. Handles.color = Color.white;
  198. Handles.SphereHandleCap(0, position, rotation, size, EventType.Repaint);
  199. Handles.Label(position, string.Format("Point{0}", index));
  200. //检测变更
  201. EditorGUI.BeginChangeCheck();
  202. //坐标操作柄
  203. position = Handles.PositionHandle(position, rotation);
  204. //变更检测结束 如果发生变更 更新路径点
  205. if (EditorGUI.EndChangeCheck())
  206. {
  207. //记录操作
  208. Undo.RecordObject(path, "Position Changed");
  209. //全局转局部坐标
  210. point.position = path.transform.InverseTransformPoint(position);
  211. //更新路径点
  212. path.Points[index] = point;
  213. path.Refrush();
  214. }
  215. }
  216. //控制点操作柄绘制
  217. private void DrawTangentHandle(int index)
  218. {
  219. BezierCurvePoint point = path.Points[index];
  220. //局部转全局坐标
  221. Vector3 cp = path.transform.TransformPoint(point.position + point.tangent);
  222. //操作柄的旋转类型
  223. Quaternion rotation = Tools.pivotRotation == PivotRotation.Local
  224. ? path.transform.rotation : Quaternion.identity;
  225. //操作柄的大小
  226. float size = HandleUtility.GetHandleSize(cp) * sphereHandleCapSize;
  227. //在该控制点绘制一个球形
  228. Handles.color = Color.yellow;
  229. Handles.SphereHandleCap(0, cp, rotation, size, EventType.Repaint);
  230. //检测变更
  231. EditorGUI.BeginChangeCheck();
  232. //坐标操作柄
  233. cp = Handles.PositionHandle(cp, rotation);
  234. //变更检测结束 如果发生变更 更新路径点
  235. if (EditorGUI.EndChangeCheck())
  236. {
  237. //记录操作
  238. Undo.RecordObject(path, "Control Point Changed");
  239. //全局转局部坐标
  240. point.tangent = path.transform.InverseTransformPoint(cp) - point.position;
  241. //更新路径点
  242. path.Points[index] = point;
  243. path.Refrush();
  244. }
  245. }
  246. //路径点操作柄绘制
  247. private void DrawTargetPointHandle()
  248. {
  249. //局部转全局坐标
  250. Vector3 position = path.targetPoint;
  251. //操作柄的旋转类型
  252. Quaternion rotation = Tools.pivotRotation == PivotRotation.Local
  253. ? path.transform.rotation : Quaternion.identity;
  254. //操作柄的大小
  255. float size = HandleUtility.GetHandleSize(position) * sphereHandleCapSize;
  256. //在该路径点绘制一个球形
  257. Handles.color = Color.blue;
  258. Handles.SphereHandleCap(0, position, rotation, size, EventType.Repaint);
  259. Handles.Label(position, string.Format("目标点"));
  260. //检测变更
  261. EditorGUI.BeginChangeCheck();
  262. //坐标操作柄
  263. position = Handles.PositionHandle(position, rotation);
  264. //变更检测结束 如果发生变更 更新路径点
  265. if (EditorGUI.EndChangeCheck())
  266. {
  267. //记录操作
  268. Undo.RecordObject(path, "TargetPoint Changed");
  269. //更新目标点
  270. path.targetPoint = position;
  271. path.Refrush();
  272. }
  273. }
  274. }
  275. #endif
  276. }