Unity扩展 UI线段绘制组件——UI上的LineRenderer

原理:

利用 Graphic 类重写 OnPopulateMesh 方法类绘制自定义顶点的面片从而组成一条线。

MaskableGraphic 类继承自 Graphic,并且可以实现“可遮罩图形”,方便在列表中使用。

绘制图形API:

// 添加顶点,第一个添加的顶点索引为0,第二个添加的顶点为1,依次.....

AddVert

// 绘制三角形,GPU绘制时会按照输入的顶点下标的顺序绘制一个三角形

AddTriangle

// 添加一个矩形

AddUIVertexQuad

// 批量添加顶点

AddUIVertexStream

// 批量添加三角形顶点,长度必须是3的倍数

AddUIVertexTriangleStream

组件功能说明:

1、设置线段宽度

2、切换曲线和直线绘制模式

3、在曲线模式下,可以控制曲线的片段数量

4、实时刷新,根据目标点的位置实时更新线条

具体实现可直接复制代码使用,具体实现已添加备注。

完整代码:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;namespace XLine
{/// <summary>/// VertexHelper 扩展类型/// </summary>static class VertexHelperEx{#region Bezierclass Bezier{private Vector2 _point0, _point1, _point2, _point3;/// <summary>/// /// </summary>/// <param name="p"></param>/// <exception cref="ArgumentException">x0,y0,x1,y1,x2,y2,x3,y3</exception>public Bezier(params float[] p){if (p.Length < 8){throw new ArgumentException("参数数量错误,需要8个参数(4个Vector2)");}_point0 = new Vector2(p[0], p[1]);_point1 = new Vector2(p[2], p[3]);_point2 = new Vector2(p[4], p[5]);_point3 = new Vector2(p[6], p[7]);}public Bezier(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3){_point0 = p0;_point1 = p1;_point2 = p2;_point3 = p3;}public float X(float t){var it = 1 - t;return it * it * it * _point0.x +3 * it * it * t * _point1.x +3 * it * t * t * _point2.x +t * t * t * _point3.x;}public float Y(float t){var it = 1 - t;return it * it * it * _point0.y +3 * it * it * t * _point1.y +3 * it * t * t * _point2.y +t * t * t * _point3.y;}public float SpeedX(float t){var it = 1 - t;return -3 * _point0.x * it * it +3 * _point1.x * it * it - 6 * _point1.x * it * t + 6 * _point2.x * it * t - 3 * _point2.x * t * t + 3 * _point3.x * t * t;}public float SpeedY(float t){var it = 1 - t;return -3 * _point0.y * it * it +3 * _point1.y * it * it - 6 * _point1.y * it * t + 6 * _point2.y * it * t - 3 * _point2.y * t * t + 3 * _point3.y * t * t;}private float SpeedXY(float t){return Mathf.Sqrt(Mathf.Pow(SpeedX(t), 2) + Mathf.Pow(SpeedY(t), 2));}}#endregion#region Toolsprivate static Vector2 Direction(this Vector2 v, Vector2 pos){return (pos - v).normalized;}private static Vector2 Rotate90(this Vector2 v, int dir = 1){var x = v.x;if (dir >= 0){v.x = -v.y;v.y = x;}else{v.x = v.y;v.y = -x;}return v;}private static Vector2 Copy(this Vector2 v){return new Vector2(v.x, v.y);}#endregion/// <summary>/// 生成顶点/// </summary>/// <param name="lineColor"></param>/// <param name="vertPos"></param>/// <returns></returns>private static UIVertex[] UIVertexQuad(Color lineColor, params Vector2[] vertPos){var vs = new UIVertex[4];var uv = new Vector2[4];uv[0] = new Vector2(0, 0);uv[1] = new Vector2(0, 1);uv[2] = new Vector2(1, 0);uv[3] = new Vector2(1, 1);for (var i = 0; i < 4; i++){var v = UIVertex.simpleVert;v.color = lineColor;v.position = vertPos[i];v.uv0 = uv[i];vs[i] = v;}return vs;}/// <summary>/// 绘制多点直线/// </summary>/// <param name="vh"></param>/// <param name="points"></param>/// <param name="width"></param>/// <param name="lineColor"></param>public static void DrawLines(this VertexHelper vh, List<Vector2> points, float width, Color lineColor){for (int i = 0; i < points.Count - 1; i++){DrawLine(vh, points[i], points[i + 1], width, lineColor);}}// 直接绘制两点之间的线段public static void DrawLine(VertexHelper vh, Vector2 start, Vector2 end, float width, Color lineColor){var direction = end.Direction(start);var normal = direction.Rotate90() * (width * 0.5f);UIVertex[] quadVerts = UIVertexQuad(lineColor, start + normal, end + normal, end - normal, start - normal);vh.AddUIVertexQuad(quadVerts);}#region 绘制贝塞尔曲线private const float InterpolationV2 = 0.6f;private const float Interpolation = 0.5f;private static readonly List<Bezier> Beziers = new List<Bezier>();/// <summary>/// 中心点/// </summary>private static readonly List<Vector2> MidPoints = new List<Vector2>();/// <summary>/// 控制点/// </summary>private static readonly List<Vector2> CtrlPoints = new List<Vector2>();/// <summary>/// 绘制贝塞尔曲线/// </summary>/// <param name="vh"></param>/// <param name="points"></param>/// <param name="segment"></param>/// <param name="width"></param>/// <param name="lineColor"></param>public static void DrawBeziers(this VertexHelper vh, List<Vector2> points, float segment, float width, Color lineColor){ConvertBeziers(points);if (Beziers.Count <= 0) return;foreach (var bezier in Beziers){DrawBezier(vh, bezier, segment, width, lineColor);}}private static void DrawBezier(VertexHelper vh, Bezier bezier, float segment, float width, Color lineColor){var leftPos = new List<Vector2>();var rightPos = new List<Vector2>();for (var i = 0; i <= segment; i++){var t = i / segment;var t2 = Interpolation * width;var bezierPos = new Vector2(bezier.X(t), bezier.Y(t));var bezierSpeed = new Vector2(bezier.SpeedX(t), bezier.SpeedY(t));var offsetA = bezierSpeed.normalized.Rotate90() * t2;var offsetB = bezierSpeed.normalized.Rotate90(-1) * t2;leftPos.Add(bezierPos.Copy() + offsetA);rightPos.Add(bezierPos.Copy() + offsetB);}for (var j = 0; j < segment; j++){vh.AddUIVertexQuad(UIVertexQuad(lineColor, leftPos[j], leftPos[j + 1], rightPos[j + 1], rightPos[j]));}}/// <summary>/// 通过点绘制贝塞尔曲线/// </summary>/// <param name="points"></param>/// <returns></returns>private static void ConvertBeziers(List<Vector2> points){Beziers.Clear();var originCnt = points.Count - 1;MidPoints.Clear();for (var i = 0; i < originCnt; i++){var x = Mathf.Lerp(points[i].x, points[i + 1].x, Interpolation);var y = Mathf.Lerp(points[i].y, points[i + 1].y, Interpolation);MidPoints.Add(new Vector2(x, y));}CtrlPoints.Clear();CtrlPoints.Add(points[0]);for (var i = 0; i < originCnt - 1; i++){var midX = Mathf.Lerp(MidPoints[i].x, MidPoints[i + 1].x, Interpolation);var midY = Mathf.Lerp(MidPoints[i].y, MidPoints[i + 1].y, Interpolation);var originPoint = points[i + 1];var offsetX = originPoint.x - midX;var offsetY = originPoint.y - midY;CtrlPoints.Add(new Vector2(MidPoints[i].x + offsetX, MidPoints[i].y + offsetY));CtrlPoints.Add(new Vector2(MidPoints[i + 1].x + offsetX, MidPoints[i + 1].y + offsetY));CtrlPoints[i * 2 + 1] = Vector2.Lerp(originPoint, CtrlPoints[i * 2 + 1], InterpolationV2);CtrlPoints[i * 2 + 2] = Vector2.Lerp(originPoint, CtrlPoints[i * 2 + 2], InterpolationV2);}CtrlPoints.Add(points[^1]);for (var i = 0; i < originCnt; i++){var bezier = new Bezier(points[i],CtrlPoints[i * 2],CtrlPoints[i * 2 + 1],points[i + 1]);Beziers.Add(bezier);}}#endregion}[RequireComponent(typeof(CanvasRenderer))]public class UILineDraw : MaskableGraphic{/// <summary>/// 目标位置/// </summary>public List<RectTransform> targets = new List<RectTransform>();/// <summary>/// 线宽/// </summary>public float lineWidth = 10f;/// <summary>/// 片段数量,仅用于曲线/// </summary>public int segments = 10;/// <summary>/// UI转屏幕坐标/// </summary>public bool isScreen = false;/// <summary>/// UI相机,用于屏幕坐标转化/// </summary>public Camera uiCamera;/// <summary>/// 开启贝塞尔曲线/// </summary>public bool openBezier = false;/// <summary>/// 实时刷新/// </summary>public bool realTimeUpdate = true;private readonly List<Vector2> _points = new List<Vector2>();private void Update(){if (!realTimeUpdate){return;}_points.Clear();foreach (var item in targets){_points.Add(isScreen ? RectTransformUtility.WorldToScreenPoint(uiCamera, item.anchoredPosition) : item.anchoredPosition);}UpdateGeometry();}protected override void OnPopulateMesh(VertexHelper vh){// 先行清除vh.Clear();if (_points.Count <= 0) return;if (openBezier){vh.DrawBeziers(_points, segments, lineWidth, color);}else{vh.DrawLines(_points, lineWidth, color);}}/// <summary>/// 获取目标数量/// </summary>public int Count => targets.Count;/// <summary>/// 添加目标/// </summary>/// <param name="target"></param>public void AddTarget(RectTransform target){targets.Add(target);}/// <summary>/// 添加目标集合/// </summary>/// <param name="transforms"></param>public void AddTargets(IEnumerable<RectTransform> transforms){targets.AddRange(transforms);}/// <summary>/// 移除目标/// </summary>/// <param name="target"></param>public void RemoveTarget(RectTransform target){targets.Remove(target);}/// <summary>/// 清理目标/// </summary>public void ClearTargets(){targets.Clear();}}
}

面板扩展:

using UnityEditor;
using UnityEditor.UI;
using UnityEngine;namespace XLine
{[CustomEditor(typeof(UILineDraw), true)][CanEditMultipleObjects]public class UILineDrawEditor : GraphicEditor{private SerializedProperty _targetRectTransform;private SerializedProperty _lineWidth;private SerializedProperty _segments;private SerializedProperty _isScreen;private SerializedProperty _uiCamera;private SerializedProperty _openBezier;private SerializedProperty _realTimeUpdate;private bool _fade = false;protected override void OnEnable(){base.OnEnable();_fade = GetEditorPrefsBool("_fadeLines", _fade);_targetRectTransform = serializedObject.FindProperty("targets");_lineWidth = serializedObject.FindProperty("lineWidth");_segments = serializedObject.FindProperty("segments");_isScreen = serializedObject.FindProperty("isScreen");_uiCamera = serializedObject.FindProperty("uiCamera");_openBezier = serializedObject.FindProperty("openBezier");_realTimeUpdate = serializedObject.FindProperty("realTimeUpdate");}public override void OnInspectorGUI(){base.OnInspectorGUI();serializedObject.Update();EditorGUILayout.Space();// 线条功能折叠或展开EditorGUI.BeginChangeCheck();_fade = EditorGUILayout.Foldout(_fade,"Lines");if (EditorGUI.EndChangeCheck())SetEditorPrefsBool("_fadeLines", _fade);if (_fade){EditorGUI.indentLevel++;EditorGUILayout.PropertyField(_targetRectTransform,new GUIContent("目标点"));EditorGUILayout.PropertyField(_lineWidth, new GUIContent("线宽"));EditorGUILayout.PropertyField(_openBezier, new GUIContent("开启贝塞尔曲线"));if (_openBezier.boolValue){EditorGUI.indentLevel++;EditorGUILayout.PropertyField(_segments, new GUIContent("线条面片数量"));EditorGUI.indentLevel--;}EditorGUILayout.PropertyField(_isScreen, new GUIContent("UI坐标转屏幕坐标"));if (_isScreen.boolValue){EditorGUI.indentLevel++;EditorGUILayout.PropertyField(_uiCamera, new GUIContent("UI相机"));EditorGUI.indentLevel--;}EditorGUILayout.PropertyField(_realTimeUpdate, new GUIContent("实时刷新"));EditorGUI.indentLevel--;}serializedObject.ApplyModifiedProperties();}/// <summary>/// 获取编辑器指定配置/// </summary>/// <param name="key"></param>/// <param name="defaultValue"></param>/// <returns></returns>private bool GetEditorPrefsBool(string key, bool defaultValue = false){return EditorPrefs.GetBool($"{PlayerSettings.companyName}{key}", defaultValue);}/// <summary>/// 设置编辑器指定配置/// </summary>/// <param name="key"></param>/// <param name="defaultValue"></param>private void SetEditorPrefsBool(string key, bool defaultValue = false){EditorPrefs.SetBool($"{PlayerSettings.companyName}{key}", defaultValue);}}
}

演示示例:

Unity UGUI扩展 —— XLine组件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/47982.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

golang 字符编码 gbk/gb2312 utf8编码相互转换,判断字符是否gbk编码函数, 字符编码转换基础原理解析, golang默认编码utf8

虽然golang里面的默认编码都是统一的unicode utf8编码&#xff0c; 但是我们在调用外部系统提供的api时&#xff0c;就可能会遇到别人的接口提供的编码非 utf8编码&#xff0c;而是gbk/gb2312编码&#xff0c; 这时候我们就必须要将别人的gbk编码转换为go语言里面的默认编码ut…

2024年7月22日(nfs samba)

一、webserver 服务器&#xff1a;作用是发布nginx的web项目 1、安装nginx&#xff08;只下载不安装&#xff09; [rootweb_server ~]# yum -y install --downloadonly --downloaddir./soft/ nginx 2、配置一个本地的nginx仓库 [rootweb_server ~]# yum -y install createrepo…

45、PHP 实现滑动窗口的最大值

题目&#xff1a; PHP 实现滑动窗口的最大值 描述&#xff1a; 给定一个数组和滑动窗口的大小&#xff0c;找出所有滑动窗口里数值的最大值。 例如&#xff1a; 如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3&#xff0c; 那么一共存在6个滑动窗口&#xff0c; 他们的最大值…

微服务

微服务架构是一种设计方法&#xff0c;它将应用程序划分为一组独立的、可互相调用的服务&#xff0c;每个服务对应一个具体的业务功能。以下是微服务的一些关键知识点总结&#xff1a; 1. 微服务的基本概念 服务组件化&#xff1a;将应用程序分解为多个小的、独立的组件&…

基于vue3 + vite产生的 TypeError: Failed to fetch dynamically imported module

具体参考这篇衔接&#xff1a; Vue3报错&#xff1a;Failed to fetch dynamically imported module-CSDN博客 反正挺扯淡的&#xff0c;错误来源于基于ry-vue-plus来进行二次开发的时候遇到的问题。 错误起因 我创建了一个广告管理页面。然后发现访问一直在加载中。报的是这样…

昇思MindSpore 应用学习-K近邻算法实现红酒聚类-CSDN

K近邻算法实现红酒聚类-AI代码解析 本实验主要介绍使用MindSpore在部分wine数据集上进行KNN实验。 1、实验目的 了解KNN的基本概念&#xff1b;了解如何使用MindSpore进行KNN实验。 2、K近邻算法原理介绍 K近邻算法&#xff08;K-Nearest-Neighbor, KNN&#xff09;是一种…

立创梁山派--移植开源的SFUD万能的串行 Flash 通用驱动库

SFUD是什么 关于SFUD库的介绍&#xff0c;其开源链接(gitee,github)已经详细的阐述了. 这里是截取自它的一部分介绍&#xff1a; SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多&#xff0c;各个 Flash 的规格及命令存在差异&#xff0c; SF…

一次搞定!中级软件设计师备考通关秘籍

大家好&#xff0c;我是小欧&#xff01; 今天我们来聊聊软考这个话题。要是你准备参加计算机技术与软件专业技术资格&#xff08;软考&#xff09;&#xff0c;那么这篇文章就是为你量身定做的。话不多说&#xff0c;咱们直接进入正题。 什么是软考&#xff1f; 软考&#xf…

请你谈谈:spring bean的生命周期 - 阶段4:检查Aware相关接口

在Spring框架中&#xff0c;Aware 接口系列提供了一种机制&#xff0c;允许bean在初始化过程中感知到容器中的特定对象&#xff0c;如应用上下文&#xff08;ApplicationContext&#xff09;、Bean工厂&#xff08;BeanFactory&#xff09;等。如果你有一个用户自定义的对象&am…

OpenSNN今日快讯:新型 54 轴仿人机器人、中国知网 CNKI 宣布上线新版首页、手搓复现GPT-2最初完整版本

一、科技视频资讯 人工智能】手搓复现GPT-2最初完整版本 | Andrej Karpathy | 8张H100训练24小时 | 成本仅672美元 | llm.c | C/CUDA | AI成本会下降么&#xff08;2024.7.17&#xff09; 简介&#xff1a;OpenAI的创始成员、前研究科学家安德烈卡帕西Andrej Karpathy最近又开始…

基于词级ngram的词袋模型对twitter数据进行情感分析

按照阿光的项目做出了学习笔记&#xff0c;pytorch深度学习实战项目100例 基于词级ngram的词袋模型对twitter数据进行情感分析 什么是 N 符&#xff1f; N 格是指给定文本或语音样本中 n 个项目的连续序列。这些项目可以是音素、音节、字母、单词或碱基对&#xff0c;具体取…

php 存储复杂的json格式查询(如:经纬度)

在开发中&#xff0c;有时我们可能存了一些复杂json格式不知道怎么查。我这里提供给大家参考下&#xff1a; 一、先上表数据格式&#xff08;location字段的possiton经纬度以逗号分开的&#xff09; {"title":"澳海文澜府","position":"11…

redis高可用之主从复制、哨兵以及Cluster集群

目录 一、Redis主从复制 1&#xff09;主从复制的作用 2&#xff09;主从复制流程 3&#xff09;搭建Redis主从复制 1、部署redis服务器 2、修改Redis配置文件&#xff08;所有节点操作&#xff09; 3、验证主从复制结果 二、哨兵模式 1&#xff09;哨兵的作用 2&…

ubuntu23安装tensorRT步骤记录

服务器信息&#xff1a; 1. ssh 连接信息&#xff1a;127.0.0.1 zhangsan2. 操作系统&#xff1a;Ubuntu 23.103. 显卡信息&#xff1a;NVIDIA Corporation GA102 [GeForce RTX 3090]4. cpu 架构&#xff1a;x86_64&#xff0c;13th Gen Intel(R) Core(TM) i9-13900( 使…

2401. 最长优雅子数组

Powered by:NEFU AB-IN Link 文章目录 2401. 最长优雅子数组题意思路代码 2401. 最长优雅子数组 题意 给你一个由 正 整数组成的数组 nums 。 如果 nums 的子数组中位于 不同 位置的每对元素按位 与&#xff08;AND&#xff09;运算的结果等于 0 &#xff0c;则称该子数组为…

微信小程序关于助力微短剧行业高质量发展的公告

微短剧行业已进入高质量发展的新阶段&#xff0c;在国家广播电视总局和广东省广播电视局的指导下&#xff0c;微信小程序平台始终坚持合规先行&#xff0c;主动建设行业管理规范&#xff0c;发布了《微短剧行业管理规范》&#xff0c;全面加强对于微短剧小程序的规范运营要求&a…

北醒单点激光雷达更改id和波特率以及Ubuntu20.04下CAN驱动

序言&#xff1a; 需要的硬件以及软件 1、USB-CAN分析仪使用顶配pro版本&#xff0c;带有支持ubuntu下的驱动包的&#xff0c;可以读取数据。 2、电源自备24V电源 3、单点激光雷达接线使用can线可以组网。 一、更改北醒单点激光雷达的id号和波特率 安装并运行USB-CAN分析仪自带…

elasticsearch8.14.1集群安装部署

elasticsearch安装部署&#xff0c;首先需要准备至少三台服务器&#xff0c;本例再windows11下安装三台vmware虚拟机&#xff0c;利用centOS7系统模拟服务器环境。 本例假设你已经安装了三台vmware和centOS7&#xff0c;且centOS7运行正常。接下来我们直接讲解elasticsearch下载…

vue实现f11全屏esc退出全屏

<template><div><p>页面内容</p><el-button type"primary" click"enter_full_screen" v-if"!full">进入</el-button><el-button type"primary" click"exitFullscreen" v-else>退…

SQL实战宝典:快速上手数据库查询与优化

文章目录 SQL 速成手册SQL 的主要功能1、基本查询语句2、表操作语句3、数据操作语句4、函数与聚合操作5、子查询与联接6、高级操作7、性能优化与安全性 基本查询语句表操作语句数据操作语句函数与聚合操作子查询与联接高级操作性能优化与安全性 SQL 速成手册 SQL&#xff08;S…