Files
Gen_Hack-and-Slash-Roguelit…/Client/Assets/Scripts/UI/UIBezierCurveGenerator.cs
2025-09-11 11:19:34 +08:00

176 lines
7.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
namespace UI
{
/// <summary>
/// UI贝塞尔曲线生成器。
/// 此组件用于生成和显示三次贝塞尔曲线并将其点数据传递给UILineRenderer进行绘制。
/// </summary>
// 确保当前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; // 用于近似曲线的线段数量,值越大曲线越平滑。
/// <summary>
/// 当脚本实例被启用时,或者首次加载时调用。
/// </summary>
void Awake()
{
// 初始化组件获取UILineRenderer的引用。
Initialize();
}
/// <summary>
/// 当在编辑器中修改脚本的属性时调用。
/// </summary>
void OnValidate()
{
// 初始化组件获取UILineRenderer的引用。
Initialize();
// 如果UILineRenderer组件有效则在编辑器中实时重新生成曲线。
if (lineRenderer != null)
{
GenerateCurvePoints();
}
}
/// <summary>
/// 初始化组件,获取 UILineRenderer 引用。
/// </summary>
private void Initialize()
{
// 如果UILineRenderer引用为空则尝试获取组件。
if (lineRenderer == null)
{
lineRenderer = GetComponent<UILineRenderer>();
// 如果仍然无法获取UILineRenderer组件则报错并禁用此组件。
if (lineRenderer == null)
{
Debug.LogError("UILineRenderer组件未在此GameObject上找到请添加一个。", 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)
{
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; // 返回计算出的贝塞尔曲线上点。
}
/// <summary>
/// 根据当前的控制点和段数生成贝塞尔曲线上的所有点,并更新 UILineRenderer 进行绘制。
/// </summary>
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();
}
/// <summary>
/// 提供了通过代码设置贝塞尔曲线控制点的方法,并立即刷新曲线。
/// </summary>
/// <param name="p0">新的起始点。</param>
/// <param name="p1">新的第一个控制点。</param>
/// <param name="p2">新的第二个控制点。</param>
/// <param name="p3">新的终止点。</param>
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>
/// <param name="count">新的曲线段数。</param>
public void SetSegmentCount(int count)
{
segmentCount = Mathf.Max(5, count); // 设置曲线段数确保至少为5段以保持一定平滑度。
GenerateCurvePoints(); // 更新曲线。
}
/// <summary>
/// 当组件首次添加到GameObject或在编辑器中选择“Reset”时调用。
/// 用于设置组件的默认值。
/// </summary>
private void Reset()
{
Initialize(); // 确保UILineRenderer引用已初始化。
// 获取当前GameObject的RectTransform以便根据其尺寸设置默认控制点。
var rt = GetComponent<RectTransform>();
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(); // 立即根据默认设置生成并显示曲线。
}
}
}