Unity之圆环slider

一、参考文章

Unity_圆环滑动条(圆形、弧形滑动条)_unity弧形滑动条-CSDN博客

此滑动条拖动超过360后继续往前滑动值会从0开始,正常我们超过360度时不可在滑动。

二、 超过360度不可滑动问题解决

参考HTML文章制作: https://www.cnblogs.com/pangys/p/13201808.html

下载链接

修改后的脚本: 


using OfficeOpenXml.FormulaParsing.Excel.Functions;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;[RequireComponent(typeof(RectTransform)), ExecuteInEditMode]
public class AnnularSlider : Selectable, IDragHandler, ICanvasElement
{[Serializable]public class DragEvent : UnityEvent{}[Serializable]public class DragValueEvent : UnityEvent<float>{}[SerializeField] private Image _fillImage;[SerializeField] private Image.Origin360 _fillOrigin;[SerializeField] private bool _clockwise;[SerializeField] private bool _wholeNumbers;[SerializeField] private float _minValue;[SerializeField] private float _maxValue = 1f;[SerializeField] private float _maxAngle = 360f;[SerializeField] private float _value;[SerializeField] private RectTransform _handleRect;[SerializeField] private float _radius = 10f;[SerializeField] private bool _towardCenter;[SerializeField] private DragValueEvent _onValueChanged = new DragValueEvent();[SerializeField] private DragEvent _onBeginDragged = new DragEvent();[SerializeField] private DragEvent _onDragging = new DragEvent();[SerializeField] private DragEvent _onEndDragged = new DragEvent();private bool _delayedUpdateVisuals;public Image FillImage{get { return _fillImage; }set{if (SetClass(ref _fillImage, value)){UpdateCachedReferences();UpdateVisuals();}}}public Image.Origin360 FillOrigin{get { return _fillOrigin; }set{if (SetStruct(ref _fillOrigin, value)){UpdateVisuals();}}}public bool Clockwise{get { return _clockwise; }set{if (SetStruct(ref _clockwise, value)){UpdateVisuals();}}}public bool WholeNumbers{get { return _wholeNumbers; }set{if (SetStruct(ref _wholeNumbers, value)){UpdateValue(_value);UpdateVisuals();}}}public float MinValue{get { return _minValue; }set{if (SetStruct(ref _minValue, value)){UpdateValue(_value);UpdateVisuals();}}}public float MaxValue{get { return _maxValue; }set{if (SetStruct(ref _maxValue, value)){UpdateValue(_value);UpdateVisuals();}}}public float MaxAngle{get { return _maxAngle; }set{if (SetStruct(ref _maxAngle, value)){UpdateVisuals();}}}public float Value{get{if (_wholeNumbers) return Mathf.Round(_value);return _value;}set { UpdateValue(value); }}public RectTransform HandleRect{get { return _handleRect; }set{if (SetClass(ref _handleRect, value)){UpdateVisuals();}}}public float Radius{get { return _radius; }set{if (SetStruct(ref _radius, value)){UpdateVisuals();}}}public bool TowardCenter{get { return _towardCenter; }set{if (SetStruct(ref _towardCenter, value)){UpdateVisuals();}}}public DragValueEvent OnValueChanged{get { return _onValueChanged; }set { _onValueChanged = value; }}public DragEvent OnBeginDragged{get { return _onBeginDragged; }set { _onBeginDragged = value; }}public DragEvent OnDragging{get { return _onDragging; }set { _onDragging = value; }}public DragEvent OnEndDragged{get { return _onEndDragged; }set { _onEndDragged = value; }}public float NormalizedValue{get{if (Mathf.Approximately(_minValue, _maxValue)) return 0;return Mathf.InverseLerp(_minValue, _maxValue, Value);}set { Value = Mathf.Lerp(_minValue, _maxValue, value);}}protected override void OnEnable(){base.OnEnable();UpdateCachedReferences();UpdateValue(_value, false);UpdateVisuals();}#if UNITY_EDITORprotected override void OnValidate(){base.OnValidate();if (WholeNumbers){_minValue = Mathf.Round(_minValue);_maxValue = Mathf.Round(_maxValue);}//Onvalidate is called before OnEnabled. We need to make sure not to touch any other objects before OnEnable is run.if (IsActive()){UpdateCachedReferences();UpdateValue(_value, false);_delayedUpdateVisuals = true;}//if (!UnityEditor.PrefabUtility.IsComponentAddedToPrefabInstance(this) && !Application.isPlaying)//    CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);}
#endifprotected virtual void Update(){if (_delayedUpdateVisuals){_delayedUpdateVisuals = false;UpdateVisuals();}}public override void OnPointerDown(PointerEventData eventData){if (MayEvent(eventData)){OnBeginDragged.Invoke();}}public double degValue;//存储public void OnDrag(PointerEventData eventData){if (!MayEvent(eventData)) return;_onDragging.Invoke();Vector2 localPoint;//鼠标在ui中的位置if (RectTransformUtility.ScreenPointToLocalPointInRectangle(_fillImage.rectTransform, eventData.position, eventData.pressEventCamera, out localPoint)){var deg = XYToDeg(localPoint.x, localPoint.y);//获取角度(用弧度制π来表示)double min = 0, max = 0;//滑块起点位置区间//根据起点位置进行换算switch (_fillOrigin){case Image.Origin360.Bottom://第四象限 起点为270°终点为360 即:[3π/2, 2π]min = Math.PI * 1.5;max = Math.PI * 2;break;case Image.Origin360.Right://deg = deg;//在第一象限为起点不换算min =max =0;break;case Image.Origin360.Top://第二象限为起点 区间[π/2,π]=>[90,180]min = Math.PI * 0.5;max = Math.PI * 2;break;case Image.Origin360.Left://第三象限为起点 区间[π,2π]=>[180,360]min = Math.PI;max = Math.PI * 2;break;default:break;}//起点位置差值换算if (deg >= min && deg <= max)deg -= min;elsedeg += (max - min);deg = Clockwise ? Math.PI * 2 - deg :  deg; //顺、逆时针方向var constMaxAngle = MaxAngle / 180;//圆的最大弧度常数var radian = deg / Math.PI; //除π得到常数值 [0,2π]/π=[0,2]var ratio = (radian / constMaxAngle) * 100;//鼠标移动的角度在圆的最大角度中的占比//限制value的最大最小值var maxValue = constMaxAngle * 100; //最大value值if (ratio > maxValue || ratio < 0)return; if (ratio >= maxValue) ratio = maxValue;if (ratio <= 0) ratio = 0;/*在圆中360°和0°是首尾相连的,为了防止鼠标滑动到360在往前滑动变成0的问题,需要进行一个计算判断。* 举例当鼠标滑动到360度时ratio和degValue值都为100,此时鼠标再往上滑动ratio值就会从0开始。* 在赋值degValue时使用Math.Abs(ratio - degValue)求两个数的绝对值,然后在设置一个最大阈值10。即可解决问题* Math.Abs(100 - 0)得出结果为100。我们设置的最大阈值为10,当鼠标再往上滑动时超出最大阈值不在赋值*/if (Math.Abs(ratio - degValue) > 10) return;//给value赋值if (degValue != Math.Round(ratio)){degValue = Math.Round(ratio);NormalizedValue = (float)degValue / 100;UpdateVisuals();}}}#region 获取角度(用弧度制π来表示)double XYToDeg(float lx, float ly){/* 这里的lx和ly已经换算过的代表了鼠标以圆中心点为原点的坐标向量,* 连接原点和鼠标坐标位置形成一个直角三角形|y轴|* * | * **      |      **        |   。   **         |  /|     *————————|—————————>*         |         *     x轴*        |        **      |      ** * | * *||    *//* 1.获取角度(用弧度制π来表示)* 利用反三角函数Math.Atan(tanθ)来获取角度* 在三角函数中 lx代表邻边,ly代表对边。根据三角函数可以得出 tanθ=ly/lx (关于直角的绘制看上方例图)* 反三角函数Arctan(ly/lx)可得出角度*/double adeg = Math.Atan(ly / lx);/* 2.将角度限制在[0 , 2π]区间。* 已知Math.Atan函数 返回的数值在[-π/2 , π/2] 换成角度是[-90,90],* 但我们需要获取[0 , 2π]即:[0,360]区间的实际值用于计算* 所以需要通过lx和ly的正负判断所在象限用于换算成[0 , 2π]区间的值*/double deg = 0;if (lx >= 0 && ly >= 0){/*第一象限: * 得到的角度在[0,90]区间,即:[0,π/2]* 不换算*/deg = adeg;}if (lx <= 0 && ly >= 0) {/*第二象限:* 得到的角度在[-90,0]区间,即:[-π/2, 0]* 需要换算为[90,180]区间 所以要+π。(在角度制中π为180)*/deg = adeg + Math.PI;}if (lx <= 0 && ly <= 0){/*第三象限:* 得到的角度在[0,90]区间,即:[0,π/2]* 需要换算为[180,270]区间 所以要+π。(在角度制中π为180)*/deg = adeg + Math.PI;}if (lx > 0 && ly < 0) {/*第四象限:* 得到的角度在[-90,00]区间,即:[-π/2, 0]* 需要换算为[270,360]区间 所以要+2π。(在角度制中π为180)*/deg = adeg + Math.PI * 2;}return deg;}#endregionpublic override void OnPointerUp(PointerEventData eventData){if (MayEvent(eventData)){OnEndDragged.Invoke();//Debug.Log("OnEndDragged");}}public void Rebuild(CanvasUpdate executing){}public void LayoutComplete(){}public void GraphicUpdateComplete(){}/// <summary>/// 返回是否可交互/// </summary>/// <returns></returns>private bool MayEvent(PointerEventData eventData){return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left;}/// <summary>/// 更新缓存引用/// </summary>private void UpdateCachedReferences(){if (_fillImage){_fillImage.type = Image.Type.Filled;_fillImage.fillMethod = Image.FillMethod.Radial360;_fillImage.fillOrigin = (int)_fillOrigin;_fillImage.fillClockwise = _clockwise;}}/// <summary>/// 更新视觉效果/// </summary>private void UpdateVisuals(){
#if UNITY_EDITORif (!Application.isPlaying)UpdateCachedReferences();
#endifvar angle = NormalizedValue * _maxAngle;if (_fillImage){_fillImage.fillAmount = angle / 360f;}if (_handleRect){_handleRect.transform.localPosition = GetPointFromFillOrigin(ref angle);if (_towardCenter){_handleRect.transform.localEulerAngles = new Vector3(0f, 0f, angle);}}}/// <summary>/// 更新Value/// </summary>/// <param name="value"></param>/// <param name="sendCallback"></param>private void UpdateValue(float value, bool sendCallback = true){value = Mathf.Clamp(value, _minValue, _maxValue);if (_wholeNumbers) value = Mathf.Round(value);if (_value.Equals(value)) return;_value = value;UpdateVisuals();if (sendCallback){_onValueChanged.Invoke(_value);//Debug.Log("OnValueChanged" + _value);}}/// <summary>/// 返回基于起始点的角度(0°~360°)/// </summary>/// <param name="point"></param>/// <returns></returns>/// <exception cref="ArgumentOutOfRangeException"></exception>private float GetAngleFromFillOrigin(Vector2 point){var angle = Mathf.Atan2(point.y, point.x) * Mathf.Rad2Deg; //相对于X轴右侧(FillOrigin.Right)的角度//转换为相对于起始点的角度switch (_fillOrigin){case Image.Origin360.Bottom:angle = _clockwise ? 270 - angle : 90 + angle;break;case Image.Origin360.Right:angle = _clockwise ? -angle : angle;break;case Image.Origin360.Top:angle = _clockwise ? 90 - angle : 270 + angle;break;case Image.Origin360.Left:angle = _clockwise ? 180 - angle : 180 + angle;break;default:throw new ArgumentOutOfRangeException();}转 360 °表示//if (angle > 360)//{//    angle -= 360;//}//if (angle < 0)//{//    angle += 360;//}return angle;}/// <summary>/// 返回基于起始点、角度、半径的位置/// </summary>/// <param name="angle"></param>/// <returns></returns>/// <exception cref="ArgumentOutOfRangeException"></exception>private Vector2 GetPointFromFillOrigin(ref float angle){//转化为相对于X轴右侧(FillOrigin.Right)的角度switch (_fillOrigin){case Image.Origin360.Bottom:angle = _clockwise ? 270 - angle : angle - 90;break;case Image.Origin360.Right:angle = _clockwise ? -angle : angle;break;case Image.Origin360.Top:angle = _clockwise ? 90 - angle : 90 + angle;break;case Image.Origin360.Left:angle = _clockwise ? 180 - angle : 180 + angle;break;default:throw new ArgumentOutOfRangeException();}var radian = angle * Mathf.Deg2Rad;return new Vector2(Mathf.Cos(radian) * _radius, Mathf.Sin(radian) * _radius);}//设置结构private static bool SetStruct<T>(ref T setValue, T value) where T : struct{if (EqualityComparer<T>.Default.Equals(setValue, value)) return false;setValue = value;return true;}private static bool SetClass<T>(ref T setValue, T value) where T : class{if (setValue == null && value == null || setValue != null && setValue.Equals(value)) return false;setValue = value;return true;}
}

三、使用方法

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

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

相关文章

SpringCloud系列(15)--Eureka自我保护

前言&#xff1a;在上一章节中我们说明了一些关于Eureka的服务发现功能&#xff0c;也用这个功能进行接口的实现&#xff0c;在本章节则介绍一些关于Eureka的自我保护 1、Eureka保护模式概述 保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。默认情况…

论文辅助笔记:LLM-Mob metric测量

0 导入库 import os import pandas as pd from sklearn.metrics import f1_score import ast import numpy as np1 基本的metric计算方式 1.1 get_acc1_f1 def get_acc1_f1(df):#计算top1 prediction的准确度和f1 scoreacc1 (df[prediction] df[ground_truth]).sum() / le…

开源数据集分享———猫脸码客

猫脸码客作为一个专注于开源数据集分享的公众号&#xff0c;致力于为广大用户提供丰富、优质的数据资源。我们精心筛选和整理各类开源数据集&#xff0c;涵盖机器学习、深度学习、自然语言处理等多个领域&#xff0c;以满足不同用户的需求。 (https://img-blog.csdnimg.cn/d98…

Exploiting CXL-based Memory for Distributed Deep Learning——论文泛读

ICPP 2022 Paper CXL论文阅读笔记整理 问题 深度学习&#xff08;DL&#xff09;正被广泛用于解决不同领域的科学应用中的复杂问题。DL应用程序使用大规模高性能计算&#xff08;HPC&#xff09;系统来训练给定的模型&#xff0c;需要消耗大量数据。这些工作负载具有很大的内…

Git for Windows 下载与安装

当前环境&#xff1a;Windows 8.1 x64 1 打开网站 https://git-scm.com/ &#xff0c;点击 Downloads 。 2 点击 Windows 。 3 选择合适的版本&#xff0c;这里选择了 32-bit Git for Windows Portable。 4 解压下载后的 PortableGit-2.44.0-32-bit.7z.exe &#xff0c;并将 P…

使用 Flask 和 WTForms 构建一个用户注册表单

在这篇技术博客中&#xff0c;我们将使用 Flask 和 WTForms 库来构建一个用户注册表单。我们将创建一个简单的 Flask 应用&#xff0c;并使用 WTForms 定义一个注册表单&#xff0c;包括用户名、密码、确认密码、邮箱、性别、城市和爱好等字段。我们还将为表单添加验证规则&…

好用的在线客服系统PHP源码(开源代码+终身使用+安装教程) 制作第一步

创建一个在线客服系统是一个涉及多个步骤的过程&#xff0c;包括前端界面设计、后端逻辑处理、数据库设计、用户认证、实时通信等多个方面。以下是使用PHP制作在线客服系统的第一步&#xff1a;需求分析和系统设计。演示&#xff1a;ym.fzapp.top 第一步&#xff1a;需求分析 确…

分布式技术在文本摘要生成中的应用

摘要 自然语言处理首先要应对的是如何表示文本以供机器处理&#xff0c;随着网络技术的发展和信息的公开&#xff0c;因特网上可供访问的数字文档成爆炸式的增长&#xff0c;文本摘要生成逐渐成为了自然语言处理领域的重要研究课题。本文主要介绍了分布式技术在文本摘要生成中…

基于springboot+vue+Mysql的广场舞团管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

猫头虎分享已解决Bug || TypeError: Cannot read property ‘map‘ of undefined**

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

智慧养猪场视频AI智能监控与可视化管理方案

在科技日新月异的今天&#xff0c;智能化、自动化已成为众多行业追求的方向。养猪业作为传统农业的重要组成部分&#xff0c;同样迎来了技术革新的春风。特别是随着人们对食品安全等问题的日益关注&#xff0c;养猪场视频监控监管方案的智能化升级显得尤为重要。 养猪场视频智…

Android11适配

1.分区存储 1.1.背景 Android 11 进一步增强了平台功能&#xff0c;为外部存储设备上的应用和用户数据提供了更好的保护。作为这项工作的一部分&#xff0c;平台引入了进一步的改进&#xff0c;以简化向分区存储的转换。 为了让用户更好地控制自己的文件&#xff0c;保护用户…

(C++) share_ptr 之循环引用

文章目录 &#x1f6a9;前言&#x1f6a9;循环引用&#x1f579;️例子1Code&#x1f62d;shared_ptr &#xff08;错误&#xff09;&#x1f602;weak_ptr &#xff08;正确&#xff09;&#x1f62d;unique_ptr &#xff08;错误&#xff09; &#x1f579;️例子2Code &…

Vu3+QuaggaJs实现web页面识别条形码

一、什么是QuaggaJs QuaggaJS是一个基于JavaScript的开源图像识别库&#xff0c;可用于识别条形码。 QuaggaJs的作用主要体现在以下几个方面&#xff1a; 实时图像处理与识别&#xff1a;QuaggaJs是一款基于JavaScript的开源库&#xff0c;它允许在Web浏览器中实现实时的图像…

LORA详解

参考论文&#xff1a; low rank adaption of llm 背景介绍&#xff1a; 自然语言处理的一个重要范式包括对一般领域数据的大规模预训练和对特定任务或领域的适应处理。在自然语言处理中的许多应用依赖于将一个大规模的预训练语言模型适配到多个下游应用上。这种适配通常是通过…

DiT论文精读Scalable Diffusion Models with Transformers CVPR2023

Scalable Diffusion Models with Transformers CVPR2023 Abstract idea 将UNet架构用Transformer代替。并且分析其可扩展性。 并且实验证明通过增加transformer的宽度和深度&#xff0c;有效降低FID 我们最大的DiT-XL/2模型在classconditional ImageNet 512、512和256、256基…

小程序AI智能名片S2B2C商城系统:四大主流商业模式深度解析与实战案例分享

在私域电商迅速崛起的大背景下&#xff0c;小程序AI智能名片S2B2C商城系统以其独特的商业模式和强大的功能&#xff0c;正成为品牌商们争相探索的新领域。在这一系统中&#xff0c;拼团模式、会员电商、社区团购和KOC营销等四种主流模式&#xff0c;为品牌商提供了多样化的营销…

【文章转载】Lance Martin的关于RAG的笔记

转载自微博黄建同学 从头开始学习 RAG&#xff0c;看Lance Martin的这篇笔记就行了&#xff0c;包含了十几篇论文和开源实现&#xff01; —— 这是一组简短的&#xff08;5-10 分钟视频&#xff09;和笔记&#xff0c;解释了我最喜欢的十几篇 RAG 论文。我自己尝试实现每个想…

C# GetField 方法应用实例

目录 关于 C# Type 类 GetField 方法应用 应用举例 心理CT设计题 类设计 DPCT类实现代码 小结 关于 C# Type 类 Type表示类型声明&#xff1a;类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义&#xff0c;以及开放或封闭构造的泛型类型。调用 t…

WPS-EXCEL:快速删除多个线条对象

问题图 我需要将线条快速删除 方法一:使用定位对象功能 使用定位功能&#xff1a;按Ctrl G打开定位对话框。在对话框中&#xff0c;点击“定位条件”。 定位对象&#xff1a;在定位条件对话框中&#xff0c;勾选“对象”选项&#xff0c;然后点击“确定”。这样&#xff0c;…