Unity draws curves on the UGUI interface

By rewriting the OnPopulateMesh method, you can customize the vertex information and realize curve drawing. The specific implementation idea is shown in the figure below:

        Draw a line segment, a is the starting point, b is the end point, the direction is as shown by the blue arrow, from a to b, the yellow rectangular box represents the width of the final drawn line segment, the line segment is ultimately determined by four vertices, here are recorded as a left , a right, b left, b right.

        If you want to calculate the vertex coordinates of the line segment, you need to use the normal direction to make the vector ab and vector3.forward form a plane, then the vector (a left, a right) is perpendicular to this plane, and the length of the vector (a left, a right) is the width of the curve, let the width be W,

Then there are:

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

a左 = a + normal*w/2;

a右 = a - normal*w/2;

b left = b + normal*w/2;

b右 = b - normal*w/2;

1. Implement basic drawing logic and use the mouse to draw full screen

1. Create a new image and set the size to full screen fill.

2. Create a new script DrawCurveByMouse. The content of the script is as follows

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();
    }
}

2. Limit the drawing range to a rectangle and still use the mouse to draw

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;
    }
}

3. By drawing function curves

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();
    }
}

Guess you like

Origin blog.csdn.net/weixin_60232873/article/details/133791088