unity在UGUI界面上绘制曲线

通过重写OnPopulateMesh方法,自定义顶点信息,实现曲线绘制,具体实现思路如下图所示:

        画一条线段,a 为起始点,b为终点,方向如蓝色箭头所示,由a指向b,黄色矩形框表示最终绘制的线段宽度,线段最终由四个顶点决定,这里分别记为a左、a右,b左,b右。

        想要计算线段的顶点坐标需要借助于法线方向,使向量ab与 vector3.forward形成一个平面,则  向量(a左,a右) 垂直于此平面,而 向量(a左,a右)的长度为曲线的宽度,设宽度为W,

则有:

               Vector3 normal = Vector3.Cross( (b-a).normalized,Vector3.forward).normalized;

a左 = a + normal*w/2;

a右 = a - normal*w/2;

b左 = b + normal*w/2;

b右 = b - normal*w/2;

一、实现基本绘制逻辑,使用鼠标全屏绘制

1、新建image,大小设置为全屏填充

2、新建脚本 DrawCurveByMouse,脚本内容如下

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DrawCurveByMouse : MaskableGraphic
{
    /// <summary>
    /// 存储整条线的所有顶点信息
    /// </summary>
    private List<UIVertex[]> vertexQuadList = new List<UIVertex[]>();

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        base.OnPopulateMesh(vh);
        vh.Clear();
        for (int i = 0; i < vertexQuadList.Count; i++)
        {
            vh.AddUIVertexQuad(vertexQuadList[i]);
        }
    }

    /// <summary>
    /// 记录上一个点的坐标信息
    /// </summary>
    private Vector3 lastPoint;

    private Vector3 lastLeft;
    private Vector3 lastRight;

    /// <summary>
    /// 宽度
    /// </summary>
    public float halfWidth;

    public float smoothStep = 10;

    /// <summary>
    /// 是否为新曲线
    /// </summary>
    private bool isNewLine = false;


    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            isNewLine = true;
            lastPoint = ScreenPointToLocalPointInRectangle(Input.mousePosition);
        }

        if (Input.GetMouseButton(0))
        {
            DrawLine(ScreenPointToLocalPointInRectangle(Input.mousePosition));
        }
    }

    /// <summary>
    /// 屏幕左边转换为矩形框中的本地坐标
    /// </summary>
    private Vector3 ScreenPointToLocalPointInRectangle(Vector2 screenPoint)
    {
        Vector2 localPoint = Vector2.zero;
        switch (canvas.renderMode)
        {
            case RenderMode.WorldSpace:
                RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, canvas.worldCamera,
                    out localPoint);
                break;
            case RenderMode.ScreenSpaceCamera:
                RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, canvas.worldCamera,
                    out localPoint);
                break;
            case RenderMode.ScreenSpaceOverlay:
                RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, null,
                    out localPoint);
                break;
        }

        return localPoint;
    }

    private void DrawLine(Vector3 curr)
    {
        Vector3 dir = curr - lastPoint;
        if (dir.magnitude < smoothStep) return;

        Vector3 normal = Vector3.Cross(dir.normalized, Vector3.forward).normalized;
        if (isNewLine)
        {
            lastLeft = lastPoint + normal * halfWidth;
            lastRight = lastPoint - normal * halfWidth;
            isNewLine = false;
        }

        Vector3 currLeft = curr + normal * halfWidth;
        Vector3 currRight = curr - normal * halfWidth;


        // Vector3 currLeft = curr + dir.normalized*  width;
        // Vector3 currRight = curr - dir.normalized* width;

        UIVertex[] quad = new UIVertex[4];

        quad[0] = new UIVertex();
        quad[0].position = lastLeft;
        quad[0].color = color;


        quad[1] = new UIVertex();
        quad[1].position = lastRight;
        quad[1].color = color;


        quad[2] = new UIVertex();
        quad[2].position = currRight;
        quad[2].color = color;


        quad[3] = new UIVertex();
        quad[3].position = currLeft;
        quad[3].color = color;

        vertexQuadList.Add(quad);
        lastLeft = currLeft;
        lastRight = currRight;
        SetAllDirty();
    }
}

二、将绘制范围限制到矩形中,仍使用鼠标绘制

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

/// <summary>
/// 在矩形框中绘制曲线
/// </summary>
public class DrawCurveInRect : MaskableGraphic, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler,
    IPointerExitHandler
{
    /// <summary>
    /// 存储整条线的所有顶点信息
    /// </summary>
    private List<UIVertex[]> vertexQuadList = new List<UIVertex[]>();

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        base.OnPopulateMesh(vh);
        vh.Clear();
        for (int i = 0; i < vertexQuadList.Count; i++)
        {
            vh.AddUIVertexQuad(vertexQuadList[i]);
        }
    }

    /// <summary>
    /// 记录上一个点的坐标信息
    /// </summary>
    private Vector3 lastPoint;

    private Vector3 lastLeft;
    private Vector3 lastRight;

    /// <summary>
    /// 宽度
    /// </summary>
    public float halfWidth = 2;

    public float smoothStep = 10;

    /// <summary>
    /// 是否为新曲线
    /// </summary>
    private bool isNewLine = false;

    private bool isEnter = false;
    private bool isDrawing = false;

    private void Update()
    {
        if (isEnter && isDrawing)
        {
            DrawLine(ScreenPointToLocalPointInRectangle(Input.mousePosition));
        }
    }

    /// <summary>
    /// 屏幕左边转换为矩形框中的本地坐标
    /// </summary>
    private Vector3 ScreenPointToLocalPointInRectangle(Vector2 screenPoint)
    {
        Vector2 localPoint = Vector2.zero;
        switch (canvas.renderMode)
        {
            case RenderMode.WorldSpace:
                RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, canvas.worldCamera,
                    out localPoint);
                break;
            case RenderMode.ScreenSpaceCamera:
                RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, canvas.worldCamera,
                    out localPoint);
                break;
            case RenderMode.ScreenSpaceOverlay:
                RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, null,
                    out localPoint);
                break;
        }

        return localPoint;
    }

    private void DrawLine(Vector3 curr)
    {
        Vector3 dir = curr - lastPoint;
        if (dir.magnitude < smoothStep) return;

        Vector3 normal = Vector3.Cross(dir.normalized, Vector3.forward).normalized;
        if (isNewLine)
        {
            lastLeft = lastPoint + normal * halfWidth;
            lastRight = lastPoint - normal * halfWidth;
            isNewLine = false;
        }

        Vector3 currLeft = curr + normal * halfWidth;
        Vector3 currRight = curr - normal * halfWidth;


        // Vector3 currLeft = curr + dir.normalized*  width;
        // Vector3 currRight = curr - dir.normalized* width;

        UIVertex[] quad = new UIVertex[4];

        quad[0] = new UIVertex();
        quad[0].position = lastLeft;
        quad[0].color = color;


        quad[1] = new UIVertex();
        quad[1].position = lastRight;
        quad[1].color = color;


        quad[2] = new UIVertex();
        quad[2].position = currRight;
        quad[2].color = color;


        quad[3] = new UIVertex();
        quad[3].position = currLeft;
        quad[3].color = color;

        vertexQuadList.Add(quad);
        lastLeft = currLeft;
        lastRight = currRight;
        SetAllDirty();
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        isNewLine = true;
        isDrawing = true;
        lastPoint = ScreenPointToLocalPointInRectangle(eventData.position);
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        isDrawing = false;
    }

    public void OnPointerEnter(PointerEventData eventData)
    {
        isEnter = true;
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        isEnter = false;
        isDrawing = false;
    }
}

三、通过绘制函数曲线

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;

/// <summary>
/// 通过函数绘制曲线
/// </summary>
public class FunctionCurve : MaskableGraphic
{
    /// <summary>
    /// 存储整条线的所有顶点信息
    /// </summary>
    private List<UIVertex[]> curveQuads = new List<UIVertex[]>();

    [Header("X轴的取值范围,")] public Vector2 rangeX;
    [Header("Y轴的取值范围,")] public Vector2 rangeY;
    /// <summary>
    /// 宽度
    /// </summary>
    public float halfWidth = 2;
    [Header("控制曲线平滑度的步长")] public float smoothStep;
    private List<UIVertex[]> pointQuads = new List<UIVertex[]>();
    [Header("取样点的颜色")] public Color simpleColor;
    [Header("取样点在轴上的值")] public float[] simples;
    /// <summary>
    /// 宽度
    /// </summary>
    public float simpleHalfWidth = 2;
   

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        base.OnPopulateMesh(vh);
        vh.Clear();
        for (int i = 0; i < curveQuads.Count; i++)
        {
            vh.AddUIVertexQuad(curveQuads[i]);
        }
        for (int i = 0; i < pointQuads.Count; i++)
        {
            vh.AddUIVertexQuad(pointQuads[i]);
        }
    }

    public List<UIVertex[]> CreatCurve()
    {
        List<UIVertex[]> vertexQuadList = new List<UIVertex[]>();
        //1、通过步长计算采样点数
        //限制步长,步长应大于单个像素代表的数值;
        float minStep = (rangeX.y - rangeX.x) / rectTransform.rect.width; //最小步长
        if (smoothStep < minStep)
        {
            smoothStep = minStep;
        }

        int count = (int)((rangeX.y - rangeX.x) / smoothStep); //采样数
        List<Vector2> points = new List<Vector2>();
        float x = 0;
        float y = 0;
        float x_rate = rectTransform.rect.width / (rangeX.y - rangeX.x);
        float y_rate = rectTransform.rect.height / (rangeY.y - rangeY.x);
        for (int i = 0; i < count; i++)
        {
            //2、计算每个采样点的值
            x = rangeX.x + smoothStep * i;
            y = GetY(x);
            //3、将采样点数值转换为坐标值
            points.Add(new Vector2(x * x_rate, y * y_rate));
        }


        for (int i = 1; i < count; i++)
        {
            if (points[i].y > rectTransform.rect.height) break;
            vertexQuadList.Add(GetQuad(points[i - 1], points[i]));
        }

        return vertexQuadList;
    }

    public List<UIVertex[]> CreatSimplePoints()
    {
        List<UIVertex[]> vertexQuadList = new List<UIVertex[]>();
        float x_rate = rectTransform.rect.width / (rangeX.y - rangeX.x);
        float y_rate = rectTransform.rect.height / (rangeY.y - rangeY.x);
        for (int i = 0; i < simples.Length; i++)
        {
            if (simples[i] >= rangeX.x && simples[i] <= rangeX.y)
            {
                vertexQuadList.Add(GetSimplePointQuad(new Vector2(simples[i] * x_rate, GetY(simples[i]) * y_rate)));
            }
        }

        return vertexQuadList;
    }

    private UIVertex[] GetQuad(Vector3 lastPoint, Vector3 curr)
    {
        Vector3 normal = Vector3.Cross((curr - lastPoint).normalized, Vector3.forward).normalized;

        Vector3 lastLeft = lastPoint + normal * halfWidth;
        Vector3 lastRight = lastPoint - normal * halfWidth;
        Vector3 currLeft = curr + normal * halfWidth;
        Vector3 currRight = curr - normal * halfWidth;

        UIVertex[] quad = new UIVertex[4];

        quad[0] = new UIVertex();
        quad[0].position = lastLeft;
        quad[0].color = color;


        quad[1] = new UIVertex();
        quad[1].position = lastRight;
        quad[1].color = color;


        quad[2] = new UIVertex();
        quad[2].position = currRight;
        quad[2].color = color;


        quad[3] = new UIVertex();
        quad[3].position = currLeft;
        quad[3].color = color;
        return quad;
    }

    private UIVertex[] GetSimplePointQuad(Vector3 point)
    {

        UIVertex[] quad = new UIVertex[4];

        quad[0] = new UIVertex();
        quad[0].position = point + new Vector3(-simpleHalfWidth, simpleHalfWidth);;
        quad[0].color = simpleColor;


        quad[1] = new UIVertex();
        quad[1].position = point + new Vector3(simpleHalfWidth, simpleHalfWidth);;
        quad[1].color = simpleColor;


        quad[2] = new UIVertex();
        quad[2].position = point + new Vector3(simpleHalfWidth, -simpleHalfWidth);;
        quad[2].color = simpleColor;


        quad[3] = new UIVertex();
        quad[3].position = point + new Vector3(-simpleHalfWidth, -simpleHalfWidth);;
        quad[3].color = simpleColor;
        return quad;
    }

    public float GetY(float x)
    {
        return Mathf.Pow(x, 2);
        return x;
    }

    [ContextMenu("Test")]
    public void Test()
    {
        curveQuads = CreatCurve();
        pointQuads = CreatSimplePoints();
        SetAllDirty();
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_60232873/article/details/133791088