123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- using UnityEngine;
- using System.Collections.Generic;
- using QFramework;
- using Org.BouncyCastle.Bcpg.OpenPgp;
- #if UNITY_EDITOR
- using UnityEditor;
- using Sirenix.OdinInspector;
- #endif
- namespace SK.Framework
- {
- /// <summary>
- /// 贝塞尔曲线路径
- /// </summary>
- public class SimpleBezierCurvePath : MonoBehaviour
- {
- [SerializeField] private BezierCurve curve;
- public float pathMaxDistance;
- [ContextMenu("刷新投影")]
- public void Refrush()
- {
- RaycastHit hit = new RaycastHit();
- targetPoints.Clear();
- //步长
- float step = 1f / curve.segments;
- //缓存上个坐标点
- Vector3 lastPos = transform.TransformPoint(curve.EvaluatePosition(0f));
- float end = (curve.points.Count - 1 < 1 ? 0 : (curve.loop ? curve.points.Count : curve.points.Count - 1)) + step * .5f;
- for (float t = step; t <= end; t += step)
- {
- //计算位置
- Vector3 p = transform.TransformPoint(curve.EvaluatePosition(t));
- if (Physics.Raycast(new Ray(p, targetPoint - p), out hit, 10))
- {
- targetPoints.Add(new MeshPoint(this.transform.InverseTransformPoint(hit.point), this.transform.InverseTransformDirection(hit.normal), 0));
- }
- }
- pathMaxDistance = 0;
- for (int t = 0; t < targetPoints.Count; t++)
- {
- if (t == 0)
- {
- lastPos = transform.TransformPoint(targetPoints[t].position);
- targetPoints[t].SetCurrentDistance(pathMaxDistance);
- continue;
- }
- Vector3 p = transform.TransformPoint(targetPoints[t].position);
- pathMaxDistance += Vector3.Distance(lastPos, p);
- targetPoints[t].SetCurrentDistance(pathMaxDistance);
- lastPos = p;
- }
- }
- public List<MeshPoint> targetPoints = new List<MeshPoint>();
- public bool Loop { get { return curve.loop; } }
- public Vector3 targetPoint;
- public List<BezierCurvePoint> Points { get { return curve.points; } }
- /// <summary>
- /// 根据归一化位置值获取对应的贝塞尔曲线上的点
- /// </summary>
- /// <param name="t">归一化位置值 [0,1]</param>
- /// <returns></returns>
- public Vector3 EvaluatePosition(float t, bool worldPos = true)
- {
- if (worldPos)
- {
- return transform.TransformPoint(curve.EvaluatePosition(t));
- }
- else
- {
- return curve.EvaluatePosition(t);
- }
- }
- [HideInInspector]
- public MeshPoint meshPoint;
- public MeshPoint GetPos(float t)
- {
- float targetflag = pathMaxDistance * t;
- for (int i = 0; i < targetPoints.Count; i++)
- {
- if (targetPoints[i].currentDistance > targetflag)
- {
- float l = targetPoints[i - 1].currentDistance / pathMaxDistance;
- float s = targetPoints[i].currentDistance / pathMaxDistance;
- float q = (t - l) / (s - l) * Vector3.Distance(targetPoints[i].position, targetPoints[i - 1].position);
- Vector3 point = transform.TransformPoint(targetPoints[i - 1].position + q * (targetPoints[i].position - targetPoints[i - 1].position).normalized);
- Vector3 normail = transform.TransformDirection(targetPoints[i - 1].normail + q * (targetPoints[i].normail - targetPoints[i - 1].normail)).normalized;
- meshPoint.position = point;
- meshPoint.normail = normail;
- return meshPoint;
- }
- }
- meshPoint.position = Vector3.zero;
- meshPoint.normail = Vector3.zero;
- return meshPoint;
- }
- #if UNITY_EDITOR
- /// <summary>
- /// 路径颜色(Gizmos)
- /// </summary>
- public Color pathColor = Color.green;
- public Color projectColor = Color.yellow;
- private void OnDrawGizmos()
- {
- if (Selection.activeGameObject != this.gameObject) return;
- if (curve.points.Count == 0) return;
- //缓存颜色
- Color cacheColor = Gizmos.color;
- //路径绘制颜色
- Gizmos.color = pathColor;
- //步长
- float step = 1f / curve.segments;
- //缓存上个坐标点
- Vector3 lastPos = transform.TransformPoint(curve.EvaluatePosition(0f));
- float end = (curve.points.Count - 1 < 1 ? 0 : (curve.loop ? curve.points.Count : curve.points.Count - 1)) + step * .5f;
- for (float t = step; t <= end; t += step)
- {
- //计算位置
- Vector3 p = transform.TransformPoint(curve.EvaluatePosition(t));
- //绘制曲线
- Gizmos.DrawLine(lastPos, p);
- Gizmos.color = new Color(1, 1, 1, 0.2f);
- Gizmos.DrawLine(p, targetPoint);
- Gizmos.color = pathColor;
- //记录
- lastPos = p;
- }
- Vector3 lastNormail = Vector3.zero;
- for (int t = 0; t < targetPoints.Count; t++)
- {
- if (t == 0)
- {
- lastPos = transform.TransformPoint(targetPoints[t].position);
- lastNormail = transform.TransformPoint(targetPoints[t].position) + transform.TransformDirection(targetPoints[t].normail).normalized * 0.03f;
- continue;
- }
- Vector3 p = transform.TransformPoint(targetPoints[t].position);
- Gizmos.DrawLine(lastPos, p);
- Gizmos.color = projectColor;
- Vector3 nomailPoint = p + transform.TransformDirection(targetPoints[t].normail).normalized * 0.03f;
- Gizmos.DrawLine(p, nomailPoint);
- Gizmos.DrawLine(lastNormail, nomailPoint);
- lastPos = p;
- lastNormail = nomailPoint;
- Gizmos.color = projectColor;
- }
- //恢复颜色
- Gizmos.color = cacheColor;
- }
- #endif
- }
- #if UNITY_EDITOR
- [CustomEditor(typeof(SimpleBezierCurvePath))]
- public class SimpleBezierCurvePathEditor : Editor
- {
- private SimpleBezierCurvePath path;
- private const float sphereHandleCapSize = .2f;
- private void OnEnable()
- {
- path = target as SimpleBezierCurvePath;
- }
- private void OnSceneGUI()
- {
- //路径点集合为空
- if (path.Points == null || path.Points.Count == 0) return;
- //当前选中工具非移动工具
- if (Tools.current != Tool.Move) return;
- //颜色缓存
- Color cacheColor = Handles.color;
- Handles.color = Color.yellow;
- DrawTargetPointHandle();
- //遍历路径点集合
- for (int i = 0; i < path.Points.Count; i++)
- {
- DrawPositionHandle(i);
- DrawTangentHandle(i);
- BezierCurvePoint point = path.Points[i];
- //局部转全局坐标 路径点、控制点
- Vector3 position = path.transform.TransformPoint(point.position);
- Vector3 controlPoint = path.transform.TransformPoint(point.position + point.tangent);
- //绘制切线
- Handles.DrawDottedLine(position, controlPoint, 1f);
- }
- //恢复颜色
- Handles.color = cacheColor;
- }
- //路径点操作柄绘制
- private void DrawPositionHandle(int index)
- {
- BezierCurvePoint point = path.Points[index];
- //局部转全局坐标
- Vector3 position = path.transform.TransformPoint(point.position);
- //操作柄的旋转类型
- Quaternion rotation = Tools.pivotRotation == PivotRotation.Local
- ? path.transform.rotation : Quaternion.identity;
- //操作柄的大小
- float size = HandleUtility.GetHandleSize(position) * sphereHandleCapSize;
- //在该路径点绘制一个球形
- Handles.color = Color.white;
- Handles.SphereHandleCap(0, position, rotation, size, EventType.Repaint);
- Handles.Label(position, string.Format("Point{0}", index));
- //检测变更
- EditorGUI.BeginChangeCheck();
- //坐标操作柄
- position = Handles.PositionHandle(position, rotation);
- //变更检测结束 如果发生变更 更新路径点
- if (EditorGUI.EndChangeCheck())
- {
- //记录操作
- Undo.RecordObject(path, "Position Changed");
- //全局转局部坐标
- point.position = path.transform.InverseTransformPoint(position);
- //更新路径点
- path.Points[index] = point;
- path.Refrush();
- }
- }
- //控制点操作柄绘制
- private void DrawTangentHandle(int index)
- {
- BezierCurvePoint point = path.Points[index];
- //局部转全局坐标
- Vector3 cp = path.transform.TransformPoint(point.position + point.tangent);
- //操作柄的旋转类型
- Quaternion rotation = Tools.pivotRotation == PivotRotation.Local
- ? path.transform.rotation : Quaternion.identity;
- //操作柄的大小
- float size = HandleUtility.GetHandleSize(cp) * sphereHandleCapSize;
- //在该控制点绘制一个球形
- Handles.color = Color.yellow;
- Handles.SphereHandleCap(0, cp, rotation, size, EventType.Repaint);
- //检测变更
- EditorGUI.BeginChangeCheck();
- //坐标操作柄
- cp = Handles.PositionHandle(cp, rotation);
- //变更检测结束 如果发生变更 更新路径点
- if (EditorGUI.EndChangeCheck())
- {
- //记录操作
- Undo.RecordObject(path, "Control Point Changed");
- //全局转局部坐标
- point.tangent = path.transform.InverseTransformPoint(cp) - point.position;
- //更新路径点
- path.Points[index] = point;
- path.Refrush();
- }
- }
- //路径点操作柄绘制
- private void DrawTargetPointHandle()
- {
- //局部转全局坐标
- Vector3 position = path.targetPoint;
- //操作柄的旋转类型
- Quaternion rotation = Tools.pivotRotation == PivotRotation.Local
- ? path.transform.rotation : Quaternion.identity;
- //操作柄的大小
- float size = HandleUtility.GetHandleSize(position) * sphereHandleCapSize;
- //在该路径点绘制一个球形
- Handles.color = Color.blue;
- Handles.SphereHandleCap(0, position, rotation, size, EventType.Repaint);
- Handles.Label(position, string.Format("目标点"));
- //检测变更
- EditorGUI.BeginChangeCheck();
- //坐标操作柄
- position = Handles.PositionHandle(position, rotation);
- //变更检测结束 如果发生变更 更新路径点
- if (EditorGUI.EndChangeCheck())
- {
- //记录操作
- Undo.RecordObject(path, "TargetPoint Changed");
- //更新目标点
- path.targetPoint = position;
- path.Refrush();
- }
- }
- }
- #endif
- }
|