首页 > 其他分享 >在Unity UI中实现UILineRenderer组件绘制线条

在Unity UI中实现UILineRenderer组件绘制线条

时间:2024-09-17 17:21:57浏览次数:12  
标签:UILineRenderer vh vertex perpendicular Unity points UI public

背景介绍

        在Unity的UI系统中,绘制线条并不像在3D世界中那样直观(使用Unity自带的LineRender组件在UI中连线并不方便,它在三维中更合适)。没有内置的工具来处理这种需求。如果你希望在UI元素之间绘制连接线(例如在UI上连接不同的图标或控件),需要自己编写逻辑。

        为了满足这种需求,我封装了一个名为UILineRenderer的组件。该组件能够处理UI中的线条绘制,并且可以适应UI元素复杂的父子关系,提供线条宽度、颜色设置等功能,非常适合在UI界面中使用。

功能简介

UILineRenderer的主要功能包括:

连接两个UI元素(当然你也可以根据我的思路拓展更多).

支持自定义线条宽度与颜色

能够根据UI元素的坐标变化实时更新线条

通过鼠标位置动态调整线条的终点位置

处理UI元素复杂的父子层级关系

        

        目前仅支持连接两个Ui元素(有一个中间状态就是只添加了一个UI元素,你可以设置鼠标作为第二个临时点,然后在合适的时候设置第二个UI元素)

        但是我重写的OnPopulateMesh逻辑实际上是支持连接多个点的,你可以修改这个组件实现你的需求.

先看效果

可以连接两个UI元素

在设置了第一个UI元素的时候,可以设置鼠标位置,从而实现始终连接鼠标(这里截图导致鼠标没了)

源码

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

[RequireComponent(typeof(CanvasRenderer))]//需要该组件才能生效
public class UILineRenderer : Graphic
{
    private List<Vector2> points = new List<Vector2>(); // 用于存储线条的点
    [SerializeField] private float lineWidth = 5f; // 线条宽度
    [SerializeField] private Color lineColor = Color.white; // 默认线条颜色

    // 每次需要重新绘制UI时调用
    protected override void OnPopulateMesh(VertexHelper vh)
    {
        vh.Clear(); // 清空当前顶点数据

        // 如果没有足够的点,则不绘制任何东西
        if (points == null || points.Count < 2)
            return;

        // 遍历每个点,创建线段
        for (int i = 0; i < points.Count - 1; i++)
        {
            Vector2 start = points[i];
            Vector2 end = points[i + 1];

            // 计算垂直方向的法线,使线条有宽度
            Vector2 direction = (end - start).normalized;
            Vector2 perpendicular = new Vector2(-direction.y, direction.x) * lineWidth / 2f;

            // 四个顶点(左下、左上、右上、右下)
            UIVertex vertex = UIVertex.simpleVert;
            vertex.color = lineColor; // 定义颜色

            // 左下
            vertex.position = new Vector3(start.x - perpendicular.x, start.y - perpendicular.y);
            vh.AddVert(vertex);

            // 左上
            vertex.position = new Vector3(start.x + perpendicular.x, start.y + perpendicular.y);
            vh.AddVert(vertex);

            // 右上
            vertex.position = new Vector3(end.x + perpendicular.x, end.y + perpendicular.y);
            vh.AddVert(vertex);

            // 右下
            vertex.position = new Vector3(end.x - perpendicular.x, end.y - perpendicular.y);
            vh.AddVert(vertex);

            // 添加两个三角形来组成矩形线条
            int index = vh.currentVertCount;
            vh.AddTriangle(index - 4, index - 3, index - 2);
            vh.AddTriangle(index - 4, index - 2, index - 1);
        }
    }

    /// <summary>
    /// 设置一个Ui元素
    /// 为什么要转换坐标?因为UI元素极可能不在同一个父物体下,存在错综复杂的父子关系
    /// 先获取UiElement世界坐标系转屏幕坐标系再转到此脚本所在的Ui坐标系
    /// </summary>
    /// <param name="uiElement"></param>
    public void AppendUIElement(RectTransform uiElement)
    {
        Vector2 localPoint;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            rectTransform, // 当前 UILineRenderer 的 RectTransform
            RectTransformUtility.WorldToScreenPoint(null, uiElement.position), // UI 元素的世界坐标转换为屏幕坐标
            null,
            out localPoint // 输出的局部坐标
        );

        // 如果已经有两个点,则移除第二个点,以保持绘制最新线条
        if (points.Count == 2)
        {
            points.RemoveAt(1);
        }

        // 添加转换后的局部坐标到点列表中
        points.Add(localPoint);

        // 标记为需要重新绘制
        SetVerticesDirty();
    }

    /// <summary>
    /// 设置鼠标位置为第二个点,此时鼠标和第一个UiElement可以构成一条线
    /// </summary>
    /// <param name="point"></param>
    public void SetMouse()
    {
        if (points.Count==2)
        {
            points.RemoveAt(1);
        }
        var mousePostion = Input.mousePosition;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, mousePostion, null, out Vector2 point);
        points.Add(point);
        SetVerticesDirty();
    }

    /// <summary>
    /// 设置线的颜色
    /// </summary>
    /// <param name="newColor"></param>
    public void SetLineColor(Color newColor)
    {
        lineColor = newColor;
        SetVerticesDirty();
    }

    /// <summary>
    /// 设置线的宽带
    /// </summary>
    /// <param name="width"></param>
    public void SetWidth(float width)
    {
        lineWidth = width;
        SetVerticesDirty();
    }

    /// <summary>
    /// 重置组件
    /// </summary>
    public void Reset()
    {
        points.Clear();
        lineColor = Color.white;
        lineWidth = 5f;
        SetVerticesDirty();
    }
}

示例

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

public class Test : MonoBehaviour
{
    public UILineRenderer t;

    public Image img1;//拖拽
    public Image img2;

    private bool keyA = false;
    // Start is called before the first frame update
    void Start()
    {
        t.AppendUIElement(img1.rectTransform);
    }

    // Update is called once per frame
    void Update()
    {
        if (keyA==false)
        {
            t.SetMouse();
        }
       
        if (Input.GetKeyDown(KeyCode.A))
        {
            keyA = true;
            t.AppendUIElement(img2.rectTransform);
        }
    }

   
}

示例结构图

挂载了UILineRender的组件和其他UI元素一样,所以要放在下面

标签:UILineRenderer,vh,vertex,perpendicular,Unity,points,UI,public
From: https://blog.csdn.net/R3333355726856/article/details/142290532

相关文章

  • Unity3D下如何播放RTSP流?
    技术背景在Unity3D中直接播放RTSP(RealTimeStreamingProtocol)流并不直接支持,因为Unity的内置多媒体组件(如AudioSource和VideoPlayer)主要设计用于处理本地文件或HTTP流,而不直接支持RTSP。所以,你可以通过一些间接的方法来实现RTSP流的播放,或者通过比较成熟的第三方插件来播。可选方......
  • Unity3D下如何播放RTSP流?
    技术背景在Unity3D中直接播放RTSP(RealTimeStreamingProtocol)流并不直接支持,因为Unity的内置多媒体组件(如AudioSource和VideoPlayer)主要设计用于处理本地文件或HTTP流,而不直接支持RTSP。所以,你可以通过一些间接的方法来实现RTSP流的播放,或者通过比较成熟的第三方插件来播。......
  • unity人工智能游戏、源码、教程(中秋特别版),完全免费和开源
    任何人不要和我说话,我不想跟任何人说话,因为我对现实世界的人类不感兴趣。谁跟我说话,我都不会理睬的。(一)游戏简介三维虚拟世界的人工智能对话。完全免费、完全开源、完整详细、通俗易懂。我把游戏、游戏源码、教程(三合一)放到了夸克网盘:链接:https://pan.quark.cn/s/65e22d51c1b......
  • Unity实战案例全解析 :PVZ 植物脚本分析
             植物都继承了Pants脚本,但是我因为没注意听讲,把Pants也挂在植物上了,所以子类的PlantEnableUpdate和PlantDisableUpdate抢不过父类,无法正确触发动画,我还找不到哪里出了问题,所以就使用了携程加while强行触发了,但是经过对源码和工程的分析比对,我发现了问题所在,......
  • Android使用LiquidFun物理引擎实现果冻碰撞效果
    一、效果展示Android使用LiquidFun物理引擎实现果冻碰撞效果二、LiquidFun物理引擎简介LiquidFun是一个由Google开发并开源的2D物理模拟库,它基于Box2D物理引擎,并扩展了流体模拟的功能。流体动力学模拟:LiquidFun提供了强大的流体动力学系统,可以模拟流体的行为,包括液体......
  • Arduino uno 实现 oled机器表情
    突然想做个表情,但网上几乎没有代码只好自己做一个,如下://接线如下:oled-------arduinoGND    GNDVCC     5VSCL     A5SDA     A4/*OLED显示图片*/#include<Wire.h>#include<Adafruit_GFX.h>#include<Adafruit_SSD1306......
  • 基于VSR-GUI的视频去字幕水印
    Video-subtitle-remover(VSR)是一款利用AI技术去除视频硬字幕的软件。本教程旨在帮助初学者从零开始,逐步完成VSR的安装与使用。在开始使用Video-subtitle-remover之前需要确保你的设备符合最低的硬件要求。VSR要求运行在具有Nvidia显卡的设备上,最低硬件要求包括NvidiaGT......
  • Unity中的三种渲染路径
    Unity中的渲染路径Unity的渲染路径在Unity里,渲染路径(RenderingPath)决定了光照是如何应用到UnityShader中的。因此,我们只有为Shader正确地选择和设置了需要的渲染路径,该shader的光照计算才可以被正确执行。unity中的渲染路径:ForwardRenderingPath(向前渲染路径)DeferredR......
  • Unity之单例模式探索
    最近在使用Unity管理远程攻击的时候涉及到了对象池,又因为需要进行脚本之间的通信,而标准通信有略显麻烦,于是使用单例的方式进行脚本之间的伪通信废话不多说,首先登场的就是最为简单的单例实现方法publicclassSingleBase:MonoBehaviour{publicstaticSingleBase......
  • 工具分享 | BurpAPIFinder - 一款Burpsuite的API敏感信息查找的burp插件,多个SRC挖掘
    0x00工具介绍BurpAPIFinder是一款Burpsuite的API敏感信息查找插件。0x01下载链接BurpAPIFinder下载链接:夸克网盘分享0x02功能介绍提取网站的URL链接和解析JS文件中的URL链接前段界面可自行定义敏感关键词、敏感url匹配界面可配置的开启主动接口探测、敏感信息获......