ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

使用UGUI原生开发连线/画线

2021-12-10 19:01:18  阅读:377  来源: 互联网

标签:原生 float Vector3 画线 public endPos position UGUI startPos


先上效果图:

 红色就是连线的效果,可以用在状态机之间的连线,也可以通过简单修改,改为在图片上涂鸦。

注:如果使用的是LineRenderer实现的话虽然也能达到这个效果,但是不能与原生UI一致,导致不能使用遮罩,层级只能在其它UI的最上层或者最下层。

 

实现方法:

   通过继承MaskableGraphic类,重写OnPopulateMesh(VertexHelper vh)方法,这样就可以在UGUI上自定义各种各样的自定义mesh了。

实现绘制mesh的方法:

  ①可以使用 VertexHelper.AddVert(UIVertex v) 这个方法增加好点位后还需要设置Triangle,调用VertexHelper.SetTriangle(int idx0, int idx1, int idx2)方法设置。

  ②也可以直接调用VertexHelper.AddUIVertexQuad(UIVertex[] verts)  此方法可以直接生成一个四边形,参数传入四边形的四个顶点,传入顺序是顺时针。

 示例图:

             v0-------v1
              |            |
             v3-------v2
                    ↑
              lineWidth

  这里使用的是第二种方法。参数传入顺序为V0 V1 V2 V3,先设置一个线条的粗细值lineWidth=2,很容易的就知道四边形四个点的坐标信息,这样就能绘制出一个四边形了。

  然而这个线并不是直线,是弯曲的线,这样才自然,所以这里需要接入三阶贝塞尔曲线。

  贝塞尔曲线详情点击:https://www.cnblogs.com/yzxhz/p/13802952.html

完整代码如下:

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

public class DrawLine : MaskableGraphic, ICanvasRaycastFilter
{

    List<List<UIVertex>> vertexQuadList = new List<List<UIVertex>>();

    public float lineWidth = 2;

    public Vector3 startPos;
    public Vector3 endPos;

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


    void Update()
    {
        //设定rect大小正好能圈住线
        rectTransform.position = endPos + (startPos - endPos) / 2f;
        rectTransform.sizeDelta = new Vector2(Mathf.Abs(endPos.x - startPos.x), Mathf.Abs(endPos.y - startPos.y));
        AddVert();
    }

    /// <summary>
    /// 增加点位
    /// </summary>
    public void AddVert()
    {

        vertexQuadList.Clear();

        //贝塞尔的两个中间点位,Start点位在左边和右边的情况区分
        Vector3 tempMiddle1Pos = startPos + Vector3.Distance(startPos, endPos) / 5f * ((startPos.x < endPos.x) ? Vector3.right : Vector3.left);
        Vector3 tempMiddle2Pos = endPos + Vector3.Distance(startPos, endPos) / 5f * ((startPos.x < endPos.x) ? Vector3.left : Vector3.right);
        //增加贝塞尔点位
        List<Vector3> bPoints = CreatThreeBezierCurve(startPos, endPos, tempMiddle1Pos, tempMiddle2Pos);
        //初始的方向设置为向上垂直方向
        Vector3 lastVerticalDir = (startPos.x < endPos.x) ? Vector3.up : Vector3.down;
        for (int i = 0; i < bPoints.Count - 1; i++)
        {
            //当前的线段方向
            Vector3 curDir = bPoints[i] - bPoints[i + 1];
            //通过当前线段方向计算垂直方向
            Vector3 curVerticalDir = Vector3.Cross(curDir.normalized, Vector3.forward).normalized;
            //已知两个相邻点位,并且已知线段的粗细,可以计算出四边形
            List<UIVertex> vertexQuad = GetTwoPointMesh(bPoints[i], bPoints[i + 1], lastVerticalDir, curVerticalDir);

            vertexQuadList.Add(vertexQuad);

            lastVerticalDir = curVerticalDir;
        }

        SetVerticesDirty();
    }


    /// <summary>
    /// 获得两个点之间的面片
    /// </summary>
    /// <param name="vertexQuad"></param>
    private List<UIVertex> GetTwoPointMesh(Vector3 _startPos, Vector3 _endPos, Vector3 beforeTwoNodeVerticalDir, Vector3 nextTwoNodeVerticalDir)
    {
        List<UIVertex> vertexQuad = new List<UIVertex>();

        //  v0-------v1
        //   |              |
        //  v3-------v2
        //           ↑
        //       width

        Vector3 v0 = _startPos - beforeTwoNodeVerticalDir * lineWidth - rectTransform.position;
        Vector3 v1 = _startPos + beforeTwoNodeVerticalDir * lineWidth - rectTransform.position;

        Vector3 v3 = _endPos - nextTwoNodeVerticalDir * lineWidth - rectTransform.position;
        Vector3 v2 = _endPos + nextTwoNodeVerticalDir * lineWidth - rectTransform.position;


        UIVertex uIVertex = new UIVertex();
        uIVertex.position = v0;
        uIVertex.color = color;
        vertexQuad.Add(uIVertex);
        UIVertex uIVertex1 = new UIVertex();
        uIVertex1.position = v1;
        uIVertex1.color = color;
        vertexQuad.Add(uIVertex1);

        UIVertex uIVertex2 = new UIVertex();
        uIVertex2.position = v2;
        uIVertex2.color = color;
        vertexQuad.Add(uIVertex2);

        UIVertex uIVertex3 = new UIVertex();
        uIVertex3.position = v3;
        uIVertex3.color = color;
        vertexQuad.Add(uIVertex3);

        return vertexQuad;
    }


    /// <summary>
    /// 设置线段的碰撞
    /// </summary>
    /// <param name="sp"></param>
    /// <param name="eventCamera"></param>
    /// <returns></returns>
    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {
        bool isEnterMesh = false;
        for (int i = 0; i < vertexQuadList.Count; i++)
        {
            if (IsContainInQuad(sp, transform.position + vertexQuadList[i][0].position, transform.position + vertexQuadList[i][1].position, transform.position + vertexQuadList[i][2].position, transform.position + vertexQuadList[i][3].position))
            {
                isEnterMesh = true;
                break;
            }

        }

        return isEnterMesh;

    }



    bool IsContainInQuad(Vector3 point, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4)
    {
        //      p1-----p2
        //      |            |
        //      |  point |
        //      |            |
        //      p4-----p3

        Vector2 p1p2 = p1 - p2;
        Vector2 p1p = p1 - point;
        Vector2 p3p4 = p3 - p4;
        Vector2 p3p = p3 - point;

        Vector2 p4p1 = p4 - p1;
        Vector2 p4p = p4 - point;
        Vector2 p2p3 = p2 - p3;
        Vector2 p2p = p2 - point;

        bool isBetweenP1P2_P3P4 = CrossAB(p1p2, p1p) * CrossAB(p3p4, p3p) > 0;
        bool isBetweenP4P1_P2P3 = CrossAB(p4p1, p4p) * CrossAB(p2p3, p2p) > 0;

        return isBetweenP1P2_P3P4 && isBetweenP4P1_P2P3;

    }

    float CrossAB(Vector2 a, Vector2 b)
    {
        return a.x * b.y - b.x * a.y;
    }


    public float nultiple = 8;
    /// <summary>
    /// 三阶贝塞尔
    /// </summary>
    /// <param name="startPoint"></param>
    /// <param name="endPoint"></param>
    /// <param name="middlePoint1"></param>
    public List<Vector3> CreatThreeBezierCurve(Vector3 startPoint, Vector3 endPoint, Vector3 middlePoint1, Vector3 middlePoint2)
    {
        List<Vector3> allPoints = new List<Vector3>();

        for (int i = 0; i < nultiple; i++)
        {
            float tempPercent = (float)i / (float)nultiple;
            float dis1 = Vector3.Distance(startPoint, middlePoint1);
            Vector3 pointL1 = startPoint + Vector3.Normalize(middlePoint1 - startPoint) * dis1 * tempPercent;
            float dis2 = Vector3.Distance(middlePoint1, middlePoint2);
            Vector3 pointL2 = middlePoint1 + Vector3.Normalize(middlePoint2 - middlePoint1) * dis2 * tempPercent;
            float dis3 = Vector3.Distance(pointL1, pointL2);
            Vector3 pointLeft = pointL1 + Vector3.Normalize(pointL2 - pointL1) * dis3 * tempPercent;


            float dis4 = Vector3.Distance(middlePoint2, endPoint);
            Vector3 pointR1 = middlePoint2 + Vector3.Normalize(endPoint - middlePoint2) * dis4 * tempPercent;
            float dis5 = Vector3.Distance(pointL2, pointR1);
            Vector3 pointRight = pointL2 + Vector3.Normalize(pointR1 - pointL2) * dis5 * tempPercent;


            float disLeftAndRight = Vector3.Distance(pointLeft, pointRight);
            Vector3 linePoint = pointLeft + Vector3.Normalize(pointRight - pointLeft) * disLeftAndRight * tempPercent;
            allPoints.Add(linePoint);
        }
        allPoints.Add(endPoint);


        return allPoints;
    }

}

 

调用的代码如下:

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

public class DragNode : MonoBehaviour, IDragHandler, IPointerDownHandler, IPointerUpHandler
{
    DrawLine drawLine;
    GameObject lineGo;

    public void OnPointerDown(PointerEventData eventData)
    {
        lineGo = new GameObject("Line");
        lineGo.transform.SetParent(transform);
        drawLine = lineGo.AddComponent<DrawLine>();
        drawLine.startPos = Input.mousePosition;
        drawLine.endPos = Input.mousePosition;
        drawLine.color = Color.red;
    }

    public void OnDrag(PointerEventData eventData)
    {
        drawLine.endPos = Input.mousePosition;
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        DestroyImmediate(lineGo);
    }
}

将这个调用代码放到Image上,运行即可使用。

 

就这样。拜拜~

标签:原生,float,Vector3,画线,public,endPos,position,UGUI,startPos
来源: https://www.cnblogs.com/yzxhz/p/15673404.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有