Files
Gen_Hack-and-Slash-Roguelit…/Client/Assets/Scripts/UI/UIBezierCurveGenerator.cs

148 lines
5.1 KiB
C#
Raw Normal View History

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(); // 在编辑器中修改参数时实时更新曲线
}
}
/// <summary>
/// 初始化组件,获取 UILineRenderer 引用
/// </summary>
private void Initialize()
{
if (lineRenderer == null)
{
lineRenderer = GetComponent<UILineRenderer>();
if (lineRenderer == null)
{
Debug.LogError("UILineRenderer component not found on this GameObject. Please add one.", this);
enabled = false; // 禁用此组件,防止空引用错误
}
}
}
/// <summary>
/// 计算三次贝塞尔曲线上的点
/// B(t) = (1-t)^3 * P0 + 3 * (1-t)^2 * t * P1 + 3 * (1-t) * t^2 * P2 + t^3 * P3
/// </summary>
/// <param name="t">参数,范围 [0, 1]</param>
/// <param name="p0">起点</param>
/// <param name="p1">第一个控制点</param>
/// <param name="p2">第二个控制点</param>
/// <param name="p3">终点</param>
/// <returns>在参数 t 处的贝塞尔曲线上点</returns>
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;
}
/// <summary>
/// 生成贝塞尔曲线的点并更新 UILineRenderer
/// </summary>
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();
}
/// <summary>
/// 提供了通过代码设置控制点并刷新曲线的方法
/// </summary>
public void SetControlPoints(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3)
{
this.P0 = p0;
this.P1 = p1;
this.P2 = p2;
this.P3 = p3;
GenerateCurvePoints();
}
/// <summary>
/// 提供了通过代码设置曲线段数并刷新曲线的方法
/// </summary>
public void SetSegmentCount(int count)
{
segmentCount = Mathf.Max(5, count); // 至少保留5段保证一定平滑度
GenerateCurvePoints();
}
// 当组件首次添加或重置时调用
void Reset()
{
Initialize(); // 确保 lineRenderer 已初始化
// 根据 RectTransform 的大小设置默认点,确保在可见范围内
RectTransform rt = GetComponent<RectTransform>();
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(); // 立即生成曲线
}
}
}