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
}