unity UGUI 鼠標畫線

using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using UnityEngine.UI;
/*
使用方法:
在場景中新建一個空的 GameObject(右鍵 -> UI -> 空對象,或直接創建空對象后添加 RectTransform 組件)
給這個新對象掛載 LineDrawer 腳本(此時會自動添加 CanvasRenderer 組件,無需手動添加 Image)
調整該對象的 RectTransform 大小和位置,使其覆蓋你需要繪制的區域
*/
[RequireComponent(typeof(CanvasRenderer))]
public class LineDrawer : MaskableGraphic, IPointerDownHandler, IDragHandler, IPointerUpHandler
{[Header("線段的寬度")][Tooltip("線段的寬度,單位為像素。值越大,繪制的線條越粗。建議取值范圍:1-20")][SerializeField] private float lineWidth = 5f;[Header("線段的填充顏色")][Tooltip("通過調整RGBA值可以改變線條的顏色和透明度")][SerializeField] private Color lineColor = Color.black;[Header("最小距離閾值")][Tooltip("鼠標拖動時添加新點的最小距離閾值(像素)。當鼠標移動距離超過此值時才會添加新點,值越小線條越精確但點數量越多,過小將影響性能")][SerializeField] private float minSegmentDistance = 5f;[Header("平滑處理")][Tooltip("是否啟用貝塞爾曲線平滑處理。勾選后線條會更流暢自然,不勾選則為直線段連接")][SerializeField] private bool drawSmoothLines = true;[Header("平滑精細度")][Tooltip("平滑線條的精細程度,控制貝塞爾曲線的分段數量。值越大曲線越平滑但性能消耗增加,建議取值范圍:3-10,僅在啟用平滑線條時生效")][SerializeField] private int smoothness = 5;[Header("多線段模式")][Tooltip("勾選后可以繪制任意數量的獨立線段,它們會同時顯示;取消勾選則每次鼠標按下會清除之前所有線條,只顯示當前正在繪制的單一線段")][SerializeField] private bool multiLineMode = true;// 線段類,存儲一條線段的所有點、顏色和粗細private class Line{public List<Vector2> points = new List<Vector2>();public Color color;public float width;}private Line currentLine = null;private List<Line> allLines = new List<Line>();private bool isDrawing = false;// 重寫顏色屬性public override Color color{get => lineColor;set{lineColor = value;SetVerticesDirty();}}// 線段粗細屬性public float LineWidth{get => lineWidth;set{lineWidth = Mathf.Max(0.1f, value);// 更新當前正在繪制的線段(如果存在)if (isDrawing && currentLine != null){currentLine.width = lineWidth;SetVerticesDirty();}}}protected override void OnPopulateMesh(VertexHelper vh){vh.Clear();// 繪制所有已完成的線段foreach (var line in allLines){if (line.points.Count < 2) continue;DrawLine(vh, line);}// 繪制當前正在繪制的線段if (currentLine != null && currentLine.points.Count >= 2){DrawLine(vh, currentLine);}}// 繪制單條線段private void DrawLine(VertexHelper vh, Line line){List<Vector2> pointsToDraw = line.points;// 如果需要平滑線段,應用貝塞爾曲線if (drawSmoothLines && line.points.Count > 2){pointsToDraw = ApplySmoothing(line.points);}// 繪制線段DrawLineSegments(vh, pointsToDraw, line.color, line.width);}// 應用平滑處理private List<Vector2> ApplySmoothing(List<Vector2> points){List<Vector2> smoothedPoints = new List<Vector2>();for (int i = 0; i < points.Count - 1; i++){Vector2 start = points[i];Vector2 end = points[i + 1];Vector2 control1 = i > 0 ? points[i] : start;Vector2 control2 = i < points.Count - 2 ? points[i + 1] : end;for (int j = 0; j <= smoothness; j++){float t = j / (float)smoothness;smoothedPoints.Add(BezierCurve(start, control1, control2, end, t));}}return smoothedPoints;}// 繪制線段(帶獨立粗細參數)private void DrawLineSegments(VertexHelper vh, List<Vector2> points, Color color, float lineWidth){int count = points.Count;if (count < 2) return;float halfWidth = lineWidth * 0.5f;// 存儲所有計算出的頂點List<Vector2> vertices = new List<Vector2>();for (int i = 0; i < count; i++){if (i == 0){// 處理第一個點AddFirstPointVertices(vertices, points[i], points[i + 1], halfWidth);}else if (i == count - 1){// 處理最后一個點AddLastPointVertices(vertices, points[i - 1], points[i], halfWidth);}else{// 處理中間點AddMidPointVertices(vertices, points[i - 1], points[i], points[i + 1], halfWidth);}}// 添加頂點到VertexHelperint startVertexIndex = vh.currentVertCount;for (int i = 0; i < vertices.Count; i++){// 計算UV,這里簡單處理為0-1范圍float uvX = Mathf.InverseLerp(0, rectTransform.rect.width, vertices[i].x);float uvY = Mathf.InverseLerp(0, rectTransform.rect.height, vertices[i].y);vh.AddVert(vertices[i], color, new Vector2(uvX, uvY));}// 添加三角形for (int i = 0; i < vertices.Count - 2; i += 2){vh.AddTriangle(startVertexIndex + i, startVertexIndex + i + 1, startVertexIndex + i + 3);vh.AddTriangle(startVertexIndex + i, startVertexIndex + i + 3, startVertexIndex + i + 2);}}// 計算貝塞爾曲線上的點private Vector2 BezierCurve(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t){float u = 1 - t;float tt = t * t;float uu = u * u;float uuu = uu * u;float ttt = tt * t;Vector2 p = uuu * p0;p += 3 * uu * t * p1;p += 3 * u * tt * p2;p += ttt * p3;return p;}// 添加第一個點的頂點(帶粗細參數)private void AddFirstPointVertices(List<Vector2> vertices, Vector2 start, Vector2 next, float halfWidth){Vector2 dir = next - start;Vector2 normal = new Vector2(-dir.y, dir.x).normalized;vertices.Add(start + normal * halfWidth);vertices.Add(start - normal * halfWidth);}// 添加最后一個點的頂點(帶粗細參數)private void AddLastPointVertices(List<Vector2> vertices, Vector2 prev, Vector2 end, float halfWidth){Vector2 dir = end - prev;Vector2 normal = new Vector2(-dir.y, dir.x).normalized;vertices.Add(end + normal * halfWidth);vertices.Add(end - normal * halfWidth);}// 添加中間點的頂點(帶粗細參數)private void AddMidPointVertices(List<Vector2> vertices, Vector2 prev, Vector2 current, Vector2 next, float halfWidth){Vector2 dir1 = current - prev;Vector2 dir2 = next - current;Vector2 normal1 = new Vector2(-dir1.y, dir1.x).normalized;Vector2 normal2 = new Vector2(-dir2.y, dir2.x).normalized;// 計算平均法線Vector2 avgNormal = (normal1 + normal2).normalized;// 計算角度float angle = Vector2.Angle(normal1, normal2) * Mathf.Deg2Rad * 0.5f;float radiusMultiplier = 1 / Mathf.Cos(angle);vertices.Add(current + avgNormal * halfWidth * radiusMultiplier);vertices.Add(current - avgNormal * halfWidth * radiusMultiplier);}// 鼠標按下開始畫線public void OnPointerDown(PointerEventData eventData){if (RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out Vector2 localPos)){// 如果不是多線段模式,清除所有線段if (!multiLineMode){allLines.Clear();}// 開始新的線段currentLine = new Line();currentLine.points.Add(localPos);currentLine.color = lineColor; // 使用當前顏色currentLine.width = lineWidth; // 使用當前粗細isDrawing = true;SetVerticesDirty();}}// 鼠標拖動時添加點public void OnDrag(PointerEventData eventData){if (!isDrawing || currentLine == null) return;if (RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out Vector2 localPos)){// 只在距離足夠遠時添加新點if (Vector2.Distance(currentLine.points[currentLine.points.Count - 1], localPos) > minSegmentDistance){currentLine.points.Add(localPos);SetVerticesDirty();}}}// 鼠標抬起結束畫線public void OnPointerUp(PointerEventData eventData){if (!isDrawing || currentLine == null) return;isDrawing = false;// 確保最后添加終點if (RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out Vector2 localPos)){if (currentLine.points.Count == 1 || Vector2.Distance(currentLine.points[currentLine.points.Count - 1], localPos) > minSegmentDistance * 0.5f){currentLine.points.Add(localPos);}}// 如果當前線段有足夠的點,添加到所有線段列表中if (currentLine.points.Count >= 2){allLines.Add(currentLine);}// 清空當前線段currentLine = null;SetVerticesDirty();}// 清除所有線條public void ClearAllLines(){allLines.Clear();currentLine = null;SetVerticesDirty();}// 設置特定線段的粗細public void SetLineWidth(int lineIndex, float width){if (lineIndex >= 0 && lineIndex < allLines.Count){allLines[lineIndex].width = Mathf.Max(0.1f, width);SetVerticesDirty();}}// 獲取特定線段的粗細public float GetLineWidth(int lineIndex){if (lineIndex >= 0 && lineIndex < allLines.Count){return allLines[lineIndex].width;}return lineWidth;}// 獲取線段數量public int GetLineCount(){return allLines.Count;}// 重寫材質獲取,使用默認UI材質public override Material material{get => defaultMaterial;set => base.material = value;}
}

參考:

https://blog.csdn.net/sdhexu/article/details/126593171?spm=1001.2014.3001.5502

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/96548.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/96548.shtml
英文地址,請注明出處:http://en.pswp.cn/web/96548.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

JSP疫情物資管理系統jbo2z--程序+源碼+數據庫+調試部署+開發環境

本系統&#xff08;程序源碼數據庫調試部署開發環境&#xff09;帶論文文檔1萬字以上&#xff0c;文末可獲取&#xff0c;系統界面在最后面。系統程序文件列表開題報告內容一、選題背景與意義新冠疫情的爆發&#xff0c;讓醫療及生活物資的調配與管理成為抗疫工作的關鍵環節。傳…

Mem0 + Milvus:為人工智能構建持久化長時記憶

作者&#xff1a;周弘懿&#xff08;錦琛&#xff09; 背景 跟 ChatGPT 對話&#xff0c;比跟真人社交還累&#xff01;真人好歹能記住你名字吧&#xff1f; 想象一下——你昨天剛把沙發位置、爆米花口味、愛看的電影都告訴了 ChatGPT&#xff0c;而它永遠是那個熱情又健忘的…

前端架構-CSR、SSR 和 SSG

將從 定義、流程、優缺點和適用場景 四個方面詳細說明它們的區別。一、核心定義縮寫英文中文核心思想CSRClient-Side Rendering客戶端渲染服務器發送一個空的 HTML 殼和 JavaScript bundle&#xff0c;由瀏覽器下載并執行 JS 來渲染內容。SSRServer-Side Rendering服務端渲染服…

主動性算法-解決點:新陳代謝

主動性[機器人與人之間的差距&#xff0c;隨著不斷地人和人工智能相處的過程中&#xff0c;機器人最終最終會掌握主動性&#xff0c;并最終走向獨立&#xff0c;也就是開始自己對于宇宙的探索。]首先:第一步讓機器人意識到自己在新陳代謝&#xff0c;人工智能每天有哪些新陳代謝…

開始理解大型語言模型(LLM)所需的數學基礎

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎&#xff1f;訂閱我們的簡報&#xff0c;深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同&#xff0c;從行業內部的深度分析和實用指南中受益。不要錯過這個機會&#xff0c;成為AI領…

prometheus安裝部署與alertmanager郵箱告警

目錄 安裝及部署知識拓展 各個組件的作用 1. Exporter&#xff08;導出器&#xff09; 2. Prometheus&#xff08;普羅米修斯&#xff09; 3. Grafana&#xff08;格拉法納&#xff09; 4. Alertmanager&#xff08;告警管理器&#xff09; 它們之間的聯系&#xff08;工…

芯科科技FG23L無線SoC現已全面供貨,為Sub-GHz物聯網應用提供最佳性價比

低功耗無線解決方案創新性領導廠商Silicon Labs&#xff08;亦稱“芯科科技”&#xff0c;NASDAQ&#xff1a;SLAB&#xff09;近日宣布&#xff1a;其第二代無線開發平臺產品組合的最新成員FG23L無線單芯片方案&#xff08;SoC&#xff09;將于9月30日全面供貨。開發套件現已上…

Flutter跨平臺工程實踐與原理透視:從渲染引擎到高質產物

&#x1f31f; Hello&#xff0c;我是蔣星熠Jaxonic&#xff01; &#x1f308; 在浩瀚無垠的技術宇宙中&#xff0c;我是一名執著的星際旅人&#xff0c;用代碼繪制探索的軌跡。 &#x1f680; 每一個算法都是我點燃的推進器&#xff0c;每一行代碼都是我航行的星圖。 &#x…

【國內電子數據取證廠商龍信科技】淺析文件頭和文件尾和隱寫

一、前言想必大家在案件中或者我們在比武中遇到了很多關于文件的隱寫問題&#xff0c;其實這一類的東西可以進行分類&#xff0c;而我們今天探討的是圖片隱寫&#xff0c;音頻隱寫&#xff0c;電子文檔隱寫&#xff0c;文件頭和文件尾的認識。二、常見文件頭和文件尾2.1圖片&am…

深度學習筆記36-yolov5s.yaml文件解讀

&#x1f368; 本文為&#x1f517;365天深度學習訓練營中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 yolov5s.yaml源文件 yolov5s.yaml源文件的代碼如下 # YOLOv5 &#x1f680; by Ultralytics, GPL-3.0 license# Parameters nc: 20 #80 # number of classe…

PostgreSQL 大對象管理指南:pg_largeobject 從原理到實踐

概述 有時候&#xff0c;你可能需要在 PostgreSQL 中管理大對象&#xff0c;例如 CLOB、BLOB 和 BFILE。PostgreSQL 中有兩種處理大對象的方法&#xff1a;一種是使用現有的數據類型&#xff0c;例如用于二進制大對象的 bytea 和用于基于字符的大對象的 text&#xff1b;另一種…

算法第四題移動零(雙指針或簡便設計),鏈路聚合(兩個交換機配置)以及常用命令

save force關閉導出dis vlandis ip int bdis int bdis int cudis thisdis ip routing-table&#xff08;查路由表&#xff09;int bridge-aggregation 1&#xff08;鏈路聚合&#xff0c;可以放入接口&#xff0c;然后一起改trunk類。&#xff09;穩定性高

告別繁瑣配置!Retrofit-Spring-Boot-Starter讓HTTP調用更優雅

01 引言 之前分享過一篇文章【像調用接口一樣調用第三方API】&#xff0c;今天迎來了新成員Retrofit。 retrofit-spring-boot-starter 是一個基于 Spring Boot 的 starter&#xff0c;它簡化了 Retrofit 在 Spring 環境中的集成和使用。Retrofit 本身是一個類型安全的 HTTP 客…

60_基于深度學習的羊群計數統計系統(yolo11、yolov8、yolov5+UI界面+Python項目源碼+模型+標注好的數據集)

目錄 項目介紹&#x1f3af; 功能展示&#x1f31f; 一、環境安裝&#x1f386; 環境配置說明&#x1f4d8; 安裝指南說明&#x1f3a5; 環境安裝教學視頻 &#x1f31f; 二、數據集介紹&#x1f31f; 三、系統環境&#xff08;框架/依賴庫&#xff09;說明&#x1f9f1; 系統環…

代理服務器是什么?怎么選擇?

代理服務器是一種位于用戶設備與目標網絡之間的中間服務器&#xff0c;通過接收用戶請求、轉發至目標網絡并將結果返回給用戶&#xff0c;實現“用戶→代理服務器→目標網絡”的間接訪問。其核心功能圍繞“網絡優化”“訪問控制”與“身份隱藏”展開&#xff0c;為個人與企業用…

代碼隨想錄刷題Day56

子集 這道題求子集&#xff0c;集合的基本運算之一&#xff0c;按照高中數學學習集合的知識&#xff0c;可以把這個找冪集的過程按照元素的個數來劃分步驟。也就是先找零個元素的子集&#xff0c;再找一個元素的子集&#xff0c;再找兩個元素的子集...一直到找N個元素的集合為…

pycharm——關于Pyqt5

PyQt5新手教程&#xff08;七萬字&#xff09; import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QPushButton, QLabel, QInputDialog, QColorDialog, QFontDialog, QFileDialog, QProgressDialog, QMessageBox from PyQt5.QtCore i…

P2678 [NOIP 2015 提高組] 跳石頭

P2678 [NOIP 2015 提高組] 跳石頭 判斷條件該怎么寫

小麥矩陣系統:一鍵批量發,多賬號同步不掉鏈

隨著互聯網的發展和社交平臺的普及&#xff0c;企業和個人用戶越來越依賴社交媒體平臺來進行信息傳播、品牌宣傳以及市場推廣。在這個信息高速流動的時代&#xff0c;如何更高效地管理多個社交平臺的賬號&#xff0c;并保持信息的同步與流暢傳播&#xff0c;成為了許多企業面臨…

JavaScript經典面試題二(函數和作用域)

目錄 一、閉包&#xff0c;使用場景 1.閉包的定義 2.閉包的實現原理 3.閉包的應用場景 &#xff08;1&#xff09;數據封裝與私有變量 &#xff08;2&#xff09;函數柯里化 &#xff08;3&#xff09;事件處理與回調 &#xff08;4&#xff09;模塊化開發 4.注意事項 …