using UnityEngine; namespace UI { // 需要引用 UILineRenderer 组件 [RequireComponent(typeof(UILineRenderer))] // 允许在编辑器模式下实时更新,便于调试 [ExecuteAlways] public class UIBezierCurveGenerator : MonoBehaviour { // UILineRenderer 组件的引用 [SerializeField] public UILineRenderer lineRenderer; // 控制点 [Header("Bezier Control Points")] public Vector2 P0; // 起点 public Vector2 P1; // 第一个控制点 public Vector2 P2; // 第二个控制点 public Vector2 P3; // 终点 [Header("Curve Settings")] [Range(5, 200)] // 限制段数范围,防止过低导致不平滑,过高导致性能问题 public int segmentCount = 50; // 曲线的平滑度,即采样的线段数量 void Awake() { Initialize(); } void OnValidate() { Initialize(); if (lineRenderer != null) { GenerateCurvePoints(); // 在编辑器中修改参数时实时更新曲线 } } /// /// 初始化组件,获取 UILineRenderer 引用 /// private void Initialize() { if (lineRenderer == null) { lineRenderer = GetComponent(); if (lineRenderer == null) { Debug.LogError("UILineRenderer component not found on this GameObject. Please add one.", this); enabled = false; // 禁用此组件,防止空引用错误 } } } /// /// 计算三次贝塞尔曲线上的点 /// B(t) = (1-t)^3 * P0 + 3 * (1-t)^2 * t * P1 + 3 * (1-t) * t^2 * P2 + t^3 * P3 /// /// 参数,范围 [0, 1] /// 起点 /// 第一个控制点 /// 第二个控制点 /// 终点 /// 在参数 t 处的贝塞尔曲线上点 private Vector2 CalculateBezierPoint(float t, Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) { float u = 1 - t; float tt = t * t; float uu = u * u; float uuu = uu * u; float ttt = tt * t; Vector2 p = uuu * p0; // (1-t)^3 * P0 p += 3 * uu * t * p1; // 3 * (1-t)^2 * t * P1 p += 3 * u * tt * p2; // 3 * (1-t) * t^2 * P2 p += ttt * p3; // t^3 * P3 return p; } /// /// 生成贝塞尔曲线的点并更新 UILineRenderer /// public void GenerateCurvePoints() { if (lineRenderer == null || segmentCount <= 0) { Debug.LogWarning("UILineRenderer is null or segmentCount is invalid. Cannot generate curve.", this); return; } // 清空 UILineRenderer 的点列表,以便重新填充 lineRenderer.points.Clear(); for (int i = 0; i <= segmentCount; i++) { float t = i / (float)segmentCount; Vector2 point = CalculateBezierPoint(t, P0, P1, P2, P3); lineRenderer.points.Add(point); } // 通知 UILineRenderer 需要重新绘制其几何体 lineRenderer.SetAllDirty(); } /// /// 提供了通过代码设置控制点并刷新曲线的方法 /// public void SetControlPoints(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) { this.P0 = p0; this.P1 = p1; this.P2 = p2; this.P3 = p3; GenerateCurvePoints(); } /// /// 提供了通过代码设置曲线段数并刷新曲线的方法 /// public void SetSegmentCount(int count) { segmentCount = Mathf.Max(5, count); // 至少保留5段,保证一定平滑度 GenerateCurvePoints(); } // 当组件首次添加或重置时调用 void Reset() { Initialize(); // 确保 lineRenderer 已初始化 // 根据 RectTransform 的大小设置默认点,确保在可见范围内 RectTransform rt = GetComponent(); float halfWidth = rt.rect.width / 2f; float halfHeight = rt.rect.height / 2f; // 设置一些默认的控制点,让曲线在UI区域内可见 // 这些是相对于 RectTransform 的局部坐标 P0 = new Vector2(-halfWidth * 0.8f, -halfHeight * 0.5f); // 左下 P1 = new Vector2(-halfWidth * 0.4f, halfHeight * 0.8f); // 左上控制点 P2 = new Vector2(halfWidth * 0.4f, halfHeight * 0.8f); // 右上控制点 P3 = new Vector2(halfWidth * 0.8f, -halfHeight * 0.5f); // 右下 segmentCount = 50; GenerateCurvePoints(); // 立即生成曲线 } } }