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 { /// /// 贝塞尔曲线路径 /// 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 targetPoints = new List(); public bool Loop { get { return curve.loop; } } public Vector3 targetPoint; public List Points { get { return curve.points; } } /// /// 根据归一化位置值获取对应的贝塞尔曲线上的点 /// /// 归一化位置值 [0,1] /// 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 /// /// 路径颜色(Gizmos) /// 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 }