using UnityEngine; namespace UI { /// /// UI贝塞尔曲线生成器。 /// 此组件用于生成和显示三次贝塞尔曲线,并将其点数据传递给UILineRenderer进行绘制。 /// // 确保当前GameObject上存在UILineRenderer组件,如果不存在则会自动添加。 [RequireComponent(typeof(UILineRenderer))] // 允许在编辑器模式下,当参数修改时实时更新曲线,便于调试和预览。 [ExecuteAlways] public class UIBezierCurveGenerator : MonoBehaviour { // 对UILineRenderer组件的引用,用于绘制生成的贝塞尔曲线。 [SerializeField] public UILineRenderer lineRenderer; // 贝塞尔曲线的四个控制点。 [Header("贝塞尔控制点")] public Vector2 P0; // 曲线的起始点。 public Vector2 P1; // 曲线的第一个控制点,影响曲线从P0开始的方向和曲率。 public Vector2 P2; // 曲线的第二个控制点,影响曲线在接近P3时的方向和曲率。 public Vector2 P3; // 曲线的终止点。 // 曲线的设置参数。 [Header("曲线设置")] [Range(5, 200)] // 限制曲线段数的范围,确保曲线平滑度和性能之间的平衡。 public int segmentCount = 50; // 用于近似曲线的线段数量,值越大曲线越平滑。 /// /// 当脚本实例被启用时,或者首次加载时调用。 /// void Awake() { // 初始化组件,获取UILineRenderer的引用。 Initialize(); } /// /// 当在编辑器中修改脚本的属性时调用。 /// void OnValidate() { // 初始化组件,获取UILineRenderer的引用。 Initialize(); // 如果UILineRenderer组件有效,则在编辑器中实时重新生成曲线。 if (lineRenderer != null) { GenerateCurvePoints(); } } /// /// 初始化组件,获取 UILineRenderer 引用。 /// private void Initialize() { // 如果UILineRenderer引用为空,则尝试获取组件。 if (lineRenderer == null) { lineRenderer = GetComponent(); // 如果仍然无法获取UILineRenderer组件,则报错并禁用此组件。 if (lineRenderer == null) { Debug.LogError("UILineRenderer组件未在此GameObject上找到,请添加一个。", 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) { var u = 1 - t; // 计算 (1-t) var tt = t * t; // 计算 t的平方 var uu = u * u; // 计算 (1-t)的平方 var uuu = uu * u; // 计算 (1-t)的立方 var ttt = tt * t; // 计算 t的立方 var 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() { // 如果UILineRenderer组件无效,则无法生成曲线。 if (!lineRenderer) { Debug.LogWarning("UILineRenderer组件为空,无法生成曲线。", this); return; } // 清空 UILineRenderer 当前的点列表,为填充新的曲线点做准备。 lineRenderer.points.Clear(); // 遍历并计算曲线上的所有采样点。 for (var i = 0; i <= segmentCount; i++) { var t = i / (float)segmentCount; // 计算当前点的归一化参数 [0, 1]。 var point = CalculateBezierPoint(t, P0, P1, P2, P3); // 根据参数 t 计算贝塞尔曲线上点坐标。 lineRenderer.points.Add(point); // 将计算出的点添加到UILineRenderer的点列表中。 } // 通知 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(); // 更新曲线。 } /// /// 当组件首次添加到GameObject或在编辑器中选择“Reset”时调用。 /// 用于设置组件的默认值。 /// private void Reset() { Initialize(); // 确保UILineRenderer引用已初始化。 // 获取当前GameObject的RectTransform,以便根据其尺寸设置默认控制点。 var rt = GetComponent(); var halfWidth = rt.rect.width / 2f; // 获取RectTransform宽度的一半。 var halfHeight = rt.rect.height / 2f; // 获取RectTransform高度的一半。 // 设置一组默认的控制点,使得曲线在UI区域内可见且具有S形或拱形。 // 这些点是相对于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(); // 立即根据默认设置生成并显示曲线。 } } }